Initialize static std::map with non copyable value in a uniformed inline initialization

荒凉一梦 提交于 2019-12-04 04:38:38

You cannot do this directly, because initializer_list has const backing for all of its elements - and they have to be copied from the initializer list into the container. That, obviously, requires copying. There's no way to emplace from an initializer list unfortunately.

In C++17, thanks to guaranteed copy elision, you can do this:

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();

This code performs no copies on non_copyable. We emplace construct inside of the map, and then beacuse get() is a prvalue, there is no copy/move from get() into value_classes. The m within get() is the object value_classes.

A slightly sneaker approach would be to abuse try_emplace() for this:

std::map<int, non_copyable> get() {
    std::map<int, non_copyable> m;
    m.try_emplace(0);
    m.try_emplace(1);
    return m;
}

try_emplace() takes the key type by itself (so you can just pass an int) and then the arguments for the value for emplacing separately, which makes for a much less verbose way of accomplishing this.

I think you need to create the object with insert_or_assign in a function and then return it:

std::map<int, ValueClass> populate()
{
    std::map<int, ValueClass> value_classes;
    value_classes.insert_or_assign(std::make_pair(0, ValueClass());
    return value_classes;
}

And your initialization becomes:

std::map<int, ValueClass> value_classes = populate();

But then, this class has a virtual destructor, which means that you want actually may actually be a std::map<int, std::unique_ptr<ValueClass>> and not a map of actual objects (not sure what these objects are going to be used for?).

Edit after the question edit:

In this case, Barrys suggestion is the one to follow, usingemplace`:

std::map<int, ValueClass> populate()
{
    std::map<int, ValueClass> value_classes;
    value_classes.emplace(1, 5);
    return value_classes;
}

Also include functional.

You simply can not use initializer_list to move an object from a non-copyable object.

Your class deletes the copy constructor & assignment operator. When you try to initialize your map or any other container with an initializer_list the initializer_list strictly forces you to reference an LValue and forbids RValue move or forward semantics.

Here is a very nice blog article that explains all of the details: knatten.org as well as a similar Q/A found here.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!