Инициализация статического std:: map<int, unique_ptr <int >> в C++

Это похоже на этот пост. Ответ, который, как мне кажется, наиболее перспективен, связан с шаблонной статической инициализацией. Вот класс из этого ответа:

template <typename T, typename U>
class create_map
{
private:
    std::map<T, U> m_map;
public:
    create_map(const T& key, const U& val)
    {
        m_map[key] = val;
    }

    create_map<T, U>& operator()(const T& key, const U& val)
    {
        m_map[key] = val;
        return *this;
    }

    operator std::map<T, U>()
    {
        return m_map;
    }
};

Использование:

std::map mymap = create_map<int, int >(1,2)(3,4)(5,6);

Это прекрасно работает для структуры или класса, а также для базовых типов. Что я хотел бы сделать, это использовать это с unique_prt<Structure\Class> как значение как это:

std::map mymap = create_map<DWORD, std::unique_ptr<Structure|Class>>(1, new Structure|Class())(2, new Structure|Class())

Я пытаюсь использовать шаблонный класс, чтобы я мог иметь значение любого типа. Из этой статьи я получил идею использовать интерфейс в качестве базового класса, а затем класс, производный от шаблона, для хранения значений любого типа. Итак, эти классы выглядят так:

class MyFieldInterface
{
public:
    int m_Size;
    virtual ~MyFieldInterface() = default;
}

template <typename T>
class MyField : public MyFieldInterface {
    T m_Value; 
}

Затем карту можно настроить так, как я описал ранее:

std::map<DWORD, unique_ptr<MyFieldInterface>> mymap;

Но попытка инициализировать его с помощью create_map не удалась:

std::map mymap = create_map<DWORD, unique_ptr<MyFieldInterface>>(1, new MyField<DWORD>())(2, new MyField<char>())(3, new MyField<WORD>())

Ошибка, которую я получаю, такова:

operator()
Error: no instance of constructor "create_map<T, U>::create_map [with T=DWORD, U=std::unique_ptr<MyFieldInterface, std::default_delete<MyFieldInterface>>]" matches the argument list
argument types are: (DWORD, MyField<DWORD>*)

Поэтому я подумал, что мне нужен конструктор и оператор (), который может правильно обрабатывать указатель. Я добавил оба к классу:

create_map(const T& key, const U* val)
{
    m_map[key] = val;
}

create_map<T, U>& operator()(const T& key, const U* val)
{
    m_map[key] = val;
    return *this;
}

Я получил ту же ошибку. Поэтому я пытался без *:

create_map(const T& key, const U val)
{
    m_map[key] = val;
}

create_map<T, U>& operator()(const T& key, const U val)
{
    m_map[key] = val;
    return *this;
}

Я получил ту же ошибку. Когда я писал это, я понял, что проблема может быть связана с наследованием, а не с оператором create_map. Можете ли вы помочь мне выяснить определение оператора () или определение базового / производного класса, которые мне нужны, чтобы заставить это работать?

Пожалуйста, ограничьте ваши ответы, чтобы не включать библиотеки Boost C++, так как мне не разрешено использовать их здесь, на работе.

Изменить: обновленный MyFieldInterface в соответствии с просьбой TC

2 ответа

Решение

Это одна из возможных реализаций:

template <typename T, typename U>
class create_map
{
private:
    std::map<T, U> m_map;
public:
    create_map(T key, U val)
    {
        m_map.emplace(std::move(key), std::move(val));
    }

    create_map&& operator()(T key, U val) &&
    {
        m_map.emplace(std::move(key), std::move(val));
        return std::move(*this);
    }

    operator std::map<T, U>() &&
    {
        return std::move(m_map);
    }
};

Обратите внимание, что аргумент берется по значению, а затем перемещается на карту с помощью emplaceи оператор преобразования, который перемещается из m_map,

Я не знаю, поддерживает ли MSVC 2012 ref-квалификаторы. Если это не так, вам нужно удалить его (это два &&s после списка параметров функции). Дело в том, чтобы обеспечить create_map следует использовать только как временный. Можно также принудить принудительно вызывать оператор преобразования только один раз, но я не делал этого в приведенном выше коде.

Теперь ваши звонки не могут использовать голые news потому что 1) он не безопасен для исключений и 2) необработанные указатели не могут быть неявно преобразованы в unique_ptrs. Просто make_unique реализация, которая не принимает во внимание массивы

namespace util {
    template<class T, class... Args>
    std::unique_ptr<T> make_unique(Args&&... args) {
        return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
    }
}

Затем вы можете изменить new MyField<DWORD>()с util::make_unique<MyField<DWORD>>()*

Демо


* Использование квалифицированного вызова отключает ADL, что может иметь неожиданный эффект при обновлении компилятора, если у вашего вызова есть аргументы. Полная реализация make_unique в соответствии со спецификацией можно найти в примере кода в N3656, make_unique предложение бумаги.

Под руководством TC и того факта, что в VC2012 нет make_unique, я перешел от использования unique_ptr<> к shared_ptr<>.

std::map mymap = create_map<DWORD, shared_ptr<MyFieldInterface>>(1, make_shared<MyField<DWORD>>())(2, make_shared<MyField<char>>())(3, make_shared<MyField<WORD>>())

Это дало мне функциональность, которую я искал, без необходимости изменения базовых классов. Хотя это работает для моих нужд, на самом деле я собираюсь отметить ответ TC как правильный, потому что он работает независимо от того, какой из них вы используете.

Другие вопросы по тегам