std::vector
can be constructed from a std::initializer_list
, and you are calling that constructor. The rules for initializer_list construction state that this constructor is aggressively preferred:
A constructor is an initializer-list constructor if its first parameter is of type std::initializer_list<E>
or reference to possibly cv-qualified std::initializer_list<E>
for some type E
, and either there are
no other parameters or else all other parameters have default arguments (8.3.6). [ Note: Initializer-list
constructors are favored over other constructors in list-initialization <...>]
Also, because of the sort of weird implementation of an initializer_list
as an array allocated under the hood, elements of the corresponding array that the std::initializer_list<E>
refers to are forced to be copy initialized (which can be elided):
An object of type std::initializer_list<E>
is constructed from an initializer list as if the implementation
allocated an array of N
elements of type E
, where N
is the number of elements in the initializer list.
Each element of that array is copy-initialized with the corresponding element of the initializer list, and
the std::initializer_list<E>
object is constructed to refer to that array
(Both references above from N3337 [dcl.init.list])
However, in your first example the copies can/are elided despite the name ([dcl.init]/14) so you don't see an extra copy construction (they can also be moved) You can thank your compiler for that, because copy elision is not required in C++11 (although it is in C++17).
See [class.copy] for more details ("When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class
object...").
The final part is key:
[support.initlist] states that
An object of type initializer_list<E>
provides access to an array of objects of type const E
.
This means that the std::vector
cannot take over the memory directly; it must be copied, this is where you ultimately see the copy constructions being called.
In the second example it is as Kerrek SB stated, you prevented the copy-elision I mentioned earlier and caused an additional overhead of a move.
initializer_list
elements. The additionalmove
in your second example inhibits copy elision and so you get an additional move on top of the copy. – Lulululuabourgstd::initializer_list
instd::vector
, those elements are no longer rvalue. – Belmontev.push_back()
a few times, until the vector exceeded its capacity and had to reallocate, you would get a bunch of copies rather than moves. To prevent that you would need to mark your move constructornoexcept
. – Kirit