英文:
How to make map.emplace automatically choose constructor in c++17?
问题
In c++17 first, I have a struct like
typedef struct ST_A{
int a;
string s;
ST_A(const int a, const string&s) :a(a),s(s) {}
}st_A;
And Now I have a simple map and emplace twice in terms of 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"));
Case 2 works fine but case 1 compiles in error. So how to solve it in case 1 and make emplace
as concise as possible?
What if the map
is much more complex? Like
map<string, deque<pair<int, st_A>>> m2
, how to emplace
it as concise as possible?
What if self-define struct has more constructors? Like
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;
would it confuse the compiler if there actually is some concise way to emplace
as the above link in cppreference.
UPDATE
If my original map is map<string, deque<pair<int, st_A>>> m2
, and now I want to add an element of pair{"c1", pair{1, st_A{10, "c"}}}
. That is to say, after adding, m2 changes into map{{"c1",deque{pair{1,st_A{10, "c"}}}}
with the form of insert
codes is like
m2.insert({"c1", {{1, {10, "c"}}}});
but it may call too many ctor/copy ctor, if I want to write in a more efficient way in performance, how could it be?
英文:
In c++17 first, I have a struct like
typedef struct ST_A{
int a;
string s;
ST_A(const int a, const string&s) :a(a),s(s) {}
}st_A;
And Now I have a simple map and emplace twice in terms of
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"));
Case 2 works fine but case 1 compiles in error. So how to solve it in case 1 and make emplace
as concise as possible?
What if the map
is much more complex? Like
map<string, deque<pair<int, st_A>>> m2
, how to emplace
it as concise as possible?
What if self-define struct has more constructors?Like
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;
would it confuse the compiler if there actually is some consice way to emplace
as the above link in cppreference.
UPDATE
If my original map is map<string, deque<pair<int, st_A>>> m2
, and now I want to add an element of pair{"c1", pair{1, st_A{10, "c"}}}
.That is to say, after adding, m2 changes into map{{"c1",deque{pair{1,st_A{10, "c"}}}}
with the form of insert
codes is like
m2.insert({"c1", {{1, {10, "c"}}}});
but it may call too many ctor/copy ctor, if I want to write in a more efficient way in performance, how could it be?
答案1
得分: 5
emplace()
从传递的参数构造 std::pair<const Key, Value>
。
没有 std::piecewise_construct
,你必须传递确切的两个参数,一个用于构造键,一个用于构造值:
m1.emplace("c", st_A{10, "c"});
这违背了"emplace"的目的,因为在调用之前已经构造了值。
我的建议是使用 try_emplace
,它具有更友好的API,特别是如果键是简单的,可以实现你想要的效果:
auto [it, inserted] = m1.try_emplace(Key{key_args...}, value_args...);
auto [it, inserted] = m1.try_emplace("c", 10, "c");
通常情况下,第一个参数构造键,其余的构造值。如果 Key
构造函数接受一个参数,那么可以省略它。
返回值指示元素是否已被"inserted",或者是否已经存在具有相同键的元素。it
基本上返回 m1.at("c")
。
键始终会被构造,只有在 inserted==true
时,值参数才会被"consumed"(=moved from)。否则它们不会受影响。
示例
对于 map<string, deque<pair<int, st_A>>> m2;
,你可以使用以下方式:
m2["C"].emplace_back(1, st_A{10, "c"});
std::deque
没有接受单个元素的构造函数,聚合初始化也不起作用。不过它并不会保存任何副本,std::deque::emplace_back
已经实现了这一点。所以 m2["C"]
已经高效地完成了构造。
如果你想省略 st_A
的临时对象,可以再次使用 std::pair
的 std::piecewise_construct
:
m2["C"].emplace_back(std::piecewise_construct, std::forward_as_tuple(1),
std::forward_as_tuple(10, "c"));
英文:
emplace()
constructs std::pair<const Key,Value>
from the passed arguments.
Without std::piecewise_construct
, you have to pass exactly two arguments, one to construct the key, one to construct the value:
m1.emplace("c", st_A{10, "c"});
Which defeats the purpose of "emplacing" since you are constructing the value ahead of the call.
My recommendation is to use try_emplace
which has a more friendly API, especially if the key is simple and allows exactly what you want:
auto [it, inserted ] = m1.try_emplace(Key{key_args...}, value_args...);
auto [it, inserted ] = m1.try_emplace("c", 10, "c");
In general, the first argument constructs the key, the rest construct the value. Key
ctor can thus be omitted if it accepts just one argument.
The return value indicates whether the element has been inserted
or if there is already an element with the same key present. it
returns basically m1.at("c")
.
The key is always constructed, the value arguments are only "consumed"(=moved from) if inserted==true
. Otherwise they are untouched.
Example
For map<string, deque<pair<int, st_A>>> m2;
, you could the the following:
m2["C"].emplace_back(1, st_A{10, "c"});
std::deque
does not have a constructor that accepts a single element, aggregate initialization does not work either. Not that it would save any copies, std::deque::emplace_back
achieves that already. So m2["C"]
does the construction efficiently already.
If you want to also elide st_A
temporary, you can again fallback to std::pair
's std::piecewise_construct
:
m2["C"].emplace_back(std::piecewise_construct, std::forward_as_tuple(1),
std::forward_as_tuple(10, "c"));
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论