c++11 emplace_back and push_back syntax with struct

独自空忆成欢 提交于 2019-11-30 01:55:28
Mark Garcia

For v.emplace_back({41,42});, see how to use std::vector::emplace_back for vector<vector<int> >?


v.emplace_back(41,42); does not work because of some rules in the standard (some emphasis mine):

Table 101 — Optional sequence container operations

Expression: a.emplace_back(args)

Return type: void

Operational semantics:
Appends an object of type T constructed with std::forward<Args>(args)....

Requires: T shall be EmplaceConstructible into X from args. For vector, T shall also be MoveInsertable into X.

For a type to be EmplaceConstructible,

§ 23.2.1.13

— T is EmplaceConstructible into X from args, for zero or more arguments args, means that the following expression is well-formed:

allocator_traits<A>::construct(m, p, args);

std::allocator_traits::construct() in turn does (if possible) a.construct(p, std::forward<Args>(args)...) (where a is m in the EmplaceConstructible expression).

a.construct() here is std::allocator::construct(), which calls ::new((void *)p) U(std::forward<Args>(args)...). This is what causes the compile error.

U(std::forward<Args>(args)...) (take note of the use of direct initialization) will find a constructor of U which accepts the forwarded arguments. However, in your case, my_pair is an aggregate type, which can only be initialized with braced-initialization syntax (aggregate initialization).


v.emplace_back(my_pair{41,42}); works because it calls either the implicitly generated default copy constructor or move constructor (note that these two may not always be generated). A temporary my_pair is first constructed which goes through the same process as that of v.emplace_back(41,42);, only that the argument is an r-value my_pair.


ADDITIONAL 1:

And why does push_back(T&&) work without addition of the struct's name?

It's because of push_back's signatures. push_back()'s argument isn't deduced, which means by doing push_back({1, 2}), a temporary object with the type of the vector's element type is first created and initialized with {1, 2}. That temporary object will then be the one that is passed to push_back(T&&).


Should I just stick with push_back? Is there any reason to use emplace_back(structname{1,2,3}) instead of push_back({1,2,3}) because it will end up calling push_back(T&&) anyway, and is easier to type?

Basically, emplace* functions is meant to optimize and remove the cost of creating temporaries and copying or move constructing objects when inserting them. However, for the case of aggregate data types where doing something like emplace_back(1, 2, 3) isn't possible, and the only way you could insert them is through creating a temporary then copying or moving, then by all means prefer the leaner syntax, and go for push_back({1,2,3}), where it would basically have the same performance as that of emplace_back(structname{1,2,3}).

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