如何让map.emplace在c++17中自动选择构造函数?

f1m*_*sch 2 c++ stdmap emplace c++17

首先在 c++17 中,我有一个类似的结构

typedef struct ST_A{
    int a;
    string s;

    ST_A(const int a, const string&s) :a(a),s(s) {}
}st_A;
Run Code Online (Sandbox Code Playgroud)

现在我有一个简单的地图,并根据 cppreference放置了两次

map<string, st_A> m1;
m1.emplace("c", 10, "c");                   ---> case 1
m1.emplace(std::piecewise_construct,        ---> case 2
        std::forward_as_tuple("c"),
        std::forward_as_tuple(10, "c"));
Run Code Online (Sandbox Code Playgroud)

情况 2 工作正常,但情况 1 编译错误。那么情况1如何解决并emplace尽可能简洁呢?

如果情况map更复杂怎么办?比如 map<string, deque<pair<int, st_A>>> m2,如何尽可能emplace简洁?

如果自定义结构体有更多构造函数怎么办?比如

typedef struct ST_A{
    int a = 0;
    string s;

    ST_A(const int a) :a(a) {}
    ST_A(const string&s) :s(s) {}
    ST_A(const int a, const string&s) :a(a),s(s) {}
}st_A;
Run Code Online (Sandbox Code Playgroud)

如果实际上有一些合理的方法来emplace作为 cppreference 中的上述链接,它会让编译器感到困惑吗?


UPDATE 如果我原来的地图是map<string, deque<pair<int, st_A>>> m2,现在我想添加一个元素 。pair{"c1", pair{1, st_A{10, "c"}}}也就是说,添加后,m2 变成代码map{{"c1",deque{pair{1,st_A{10, "c"}}}} 形式insert就像

m2.insert({"c1", {{1, {10, "c"}}}});
Run Code Online (Sandbox Code Playgroud)

但它可能会调用太多的ctor/copy ctor,如果我想在性能上以更高效的方式编写,怎么可能呢?

Qui*_*mby 5

emplace()std::pair<const Key,Value>从传递的参数构造。

如果没有std::piecewise_construct,您必须传递两个参数,一个用于构造键,一个用于构造值:

m1.emplace("c", st_A{10, "c"});  
Run Code Online (Sandbox Code Playgroud)

这违背了“安置”的目的,因为您是在调用之前构建值。

我的建议是使用try_emplace具有更友好 API 的 API,特别是如果密钥很简单并且完全允许您想要的内容:

auto [it, inserted ] = m1.try_emplace(Key{key_args...}, value_args...);  
Run Code Online (Sandbox Code Playgroud)
auto [it, inserted ] = m1.try_emplace("c", 10, "c");  
Run Code Online (Sandbox Code Playgroud)

一般来说,第一个参数构造键,其余参数构造值。Key因此,如果 ctor 只接受一个参数,则可以省略。

返回值指示该元素是否已经存在inserted或者是否已经存在具有相同键的元素。it基本都回来了m1.at("c")

键总是被构造的,值参数仅在 if 时被“消耗”(=移出)inserted==true。否则它们就不会受到影响。

例子

对于map<string, deque<pair<int, st_A>>> m2;,您可以执行以下操作:

m2["C"].emplace_back(1, st_A{10, "c"});
Run Code Online (Sandbox Code Playgroud)

std::deque没有接受单个元素的构造函数,聚合初始化也不起作用。并不是说它会保存任何副本,std::deque::emplace_back已经实现了这一点。m2["C"]施工效率也已经如此。

如果您还想st_A暂时删除,您可以再次回退到std::pairs std::piecewise_construct

m2["C"].emplace_back(std::piecewise_construct, std::forward_as_tuple(1),
                     std::forward_as_tuple(10, "c"));
Run Code Online (Sandbox Code Playgroud)