Инициализируйте статический std::map с не копируемым значением при встроенной инициализации в форме
Я хотел бы инициализировать статический std::map
где значение не подлежит копированию. Я позвоню своему классу ValueClass. ValueClass имеет std::unique_ptr
как частный член, и я даже гарантирую, что ValueClass не будет копироваться путем расширения non_copyable
это выглядит следующим образом:
class non_copyable {
public:
non_copyable() = default;
protected:
virtual ~non_copyable() = default;
private:
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
};
Теперь я пытаюсь определить std::map, используя мой класс в качестве значения:
static std::map<int, ValueClass> value_classes = {
{0, ValueClass()},
{1, ValueClass() }
};
Я получаю ошибку компиляции как initializer_list
пытается скопировать этот класс.
Я пытался написать свой собственный make_map
Работать целыми в эти выходные в течение многих часов, чтобы включить инициализацию без копирования, но мне не удалось. Я пробовал это, то и другое, но ни один из них не компилируется с Visual Studio 15.9.4.
Как я могу инициализировать статический std::map, где копирование не принудительно, и инициализация унифицирована в одной функции, используя компилятор Visual Studio?
РЕДАКТИРОВАТЬ: Вот упрощенная версия сценария реальной жизни, где я пытаюсь заставить это работать (извините за отсутствие соглашения об именах и непоследовательность для случаев):
#include <iostream>
#include <map>
class non_copyable {
public:
non_copyable() = default;
protected:
virtual ~non_copyable() = default;
private:
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
};
class InnerValueClass : public non_copyable
{
public:
InnerValueClass(const int inner_number) : inner_number_(inner_number) { }
private:
int inner_number_;
};
class ValueClass : public non_copyable
{
public:
ValueClass(const int number1) : number1_(number1) { }
ValueClass(const bool condition) : condition_(condition), inner_value_(
std::make_unique<InnerValueClass>(5)) { }
private:
int number1_{};
bool condition_{};
std::unique_ptr<InnerValueClass> inner_value_{};
};
/* Inline initialization of std::map copies, this is for initialization of non-copy types*/
template <typename TKey, typename TNonCopyableValue>
class make_map_by_moving
{
typedef std::map<TKey, TNonCopyableValue> map_type;
map_type map_;
public:
make_map_by_moving(const TKey& key, TNonCopyableValue&& val)
{
map_.emplace(key, std::move(val));
}
make_map_by_moving<TKey, TNonCopyableValue>& operator()(const TKey& key, TNonCopyableValue&& val)
{
map_.emplace(key, std::move(val));
return *this;
}
operator const map_type&()
{
return map_;
}
};
static std::map<int, ValueClass> map =
make_map_by_moving<int, ValueClass>
(1, ValueClass(5))
(2, ValueClass(true));
/* It goes on like this for hundreds of lines, so I really appreciate any
solution that leave me with a clean initialization rather than calling
functions on std::map */
int main() { }
Дублирующее редактирование: Решение, представленное в этом вопросе, не работает со структурой класса, которая у меня есть. Я также ищу решение, чтобы исправить make_map_by_moving
функция, другими словами, встроенная инициализация, ответ при условии, что есть императивное решение с вызовами функций.
3 ответа
Вы не можете сделать это напрямую, потому что initializer_list
имеет const
поддержка всех его элементов - и они должны быть скопированы из списка инициализатора в контейнер. Это, очевидно, требует копирования. К сожалению, нет возможности исключить из списка инициализатора.
В C++17, благодаря гарантированному разрешению копирования, вы можете сделать это:
std::map<int, non_copyable> get() {
std::map<int, non_copyable> m;
m.emplace(std::piecewise_construct, std::tuple(0), std::tuple());
m.emplace(std::piecewise_construct, std::tuple(1), std::tuple());
return m;
}
std::map<int, non_copyable> value_classes = get();
Этот код не выполняет никаких копий на non_copyable
, Мы размещаем конструкцию внутри map
, а затем потому что get()
является prvalue, нет копирования / перемещения из get()
в value_classes
, m
в get()
это объект value_classes
,
Слегка подлым подходом было бы злоупотреблять try_emplace()
за это:
std::map<int, non_copyable> get() {
std::map<int, non_copyable> m;
m.try_emplace(0);
m.try_emplace(1);
return m;
}
try_emplace()
принимает тип ключа сам по себе (так что вы можете просто передать int
), а затем аргументы для значения для отдельного размещения, что обеспечивает гораздо менее многословный способ выполнения этого.
Я думаю, что вам нужно создать объект с insert_or_assign
в функции, а затем вернуть его:
std::map<int, ValueClass> populate()
{
std::map<int, ValueClass> value_classes;
value_classes.insert_or_assign(std::make_pair(0, ValueClass());
return value_classes;
}
И ваша инициализация становится:
std::map<int, ValueClass> value_classes = populate();
Но тогда у этого класса есть виртуальный деструктор, что означает, что вы на самом деле можете быть std::map<int, std::unique_ptr<ValueClass>>
а не карта реальных объектов (не знаете, для чего эти объекты будут использоваться?).
Редактировать после вопроса
В этом случае Барриs suggestion is the one to follow, using
emplace`:
std::map<int, ValueClass> populate()
{
std::map<int, ValueClass> value_classes;
value_classes.emplace(1, 5);
return value_classes;
}
Также включить functional
,
Вы просто не можете использовать initializer_list
в move
объект из non-copyable
объект.
Ваш класс удаляет copy constructor
& assignment operator
, Когда вы пытаетесь инициализировать ваш map
или любой другой container
с initializer_list
initializer_list
строго заставляет вас ссылаться на LValue
и запрещает RValue
семантика перемещения или продвижения вперед
Вот очень хорошая статья в блоге, которая объясняет все детали: knatten.org, а также аналогичные вопросы и ответы, найденные здесь.