Инициализация статического 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
следует использовать только как временный. Можно также принудить принудительно вызывать оператор преобразования только один раз, но я не делал этого в приведенном выше коде.
Теперь ваши звонки не могут использовать голые new
s потому что 1) он не безопасен для исключений и 2) необработанные указатели не могут быть неявно преобразованы в unique_ptr
s. Просто 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 как правильный, потому что он работает независимо от того, какой из них вы используете.