From what I understand, initializer_list
s pass everything by const-reference. It is probably not safe to move
from one. The initializer_list
constructor of a vector
will copy each of the elements.
Here are some links:
initializer_list and move semantics
No, that won't work as intended; you will still get copies. I'm pretty
surprised by this, as I'd thought that initializer_list existed to
keep an array of temporaries until they were move'd.
begin and end for initializer_list return const T *, so the result of
move in your code is T const && — an immutable rvalue reference. Such
an expression can't meaningfully be moved from. It will bind to an
function parameter of type T const & because rvalues do bind to const
lvalue references, and you will still see copy semantics.
Is it safe to move elements of a initializer list?
initializer_list only provides const access to its elements. You could
use const_cast to make that code compile, but then the moves might end
up with undefined behaviour (if the elements of the initializer_list
are truly const). So, no it is not safe to do this moving. There are
workarounds for this, if you truly need it.
Can I list-initialize a vector of move-only type?
The synopsis of in 18.9 makes it reasonably clear
that elements of an initializer list are always passed via
const-reference. Unfortunately, there does not appear to be any way of
using move-semantic in initializer list elements in the current
revision of the language.
questions regarding the design of std::initializer_list
From section 18.9 of the C++ Standard:
An object of type initializer_list provides access to an array of objects of type const E. [ Note: A pair of pointers or a pointer plus
a length would be obvious representations for initializer_list.
initializer_list is used to implement initializer lists as specified
in 8.5.4. Copying an initializer list does not copy the underlying
elements. — end note ]
I think the reason for most of these things is that
std::initializer_list isn't actually a container. It doesn't have
value semantics, it has pointer semantics. Which is made obvious by
the last portion of the quote: Copying an initializer list does not
copy the underlying elements. Seeing as they were intended solely for
the purpose of initializing things, I don't think it's that surprising
that you don't get all the niceties of more robust containers such as
tuples.
If I understand the last part correctly, it means that two sets of copies are needed since initializer_list
does not copy the underlying elements. (The previous quote is only relevant if you attempt to use an initializer_list
without copying out the elements.)
What is the underlying structure of std::initializer_list?
No, you can't move from the elements of an initializer_list, since
elements of an initializer_list are supposed to be immutable (see the
first sentence of the paragraph quoted above). That's also the reason
why only const-qualified member functions give you access to the
elements.
If you want, you can use emplace_back
:
vec.emplace_back(TS());
vec.emplace_back(TS());
vec.push_back(std::move(ts1));
vec.push_back(std::move(ts2));
std::initializer_list<TS>
and copied out => two copy-constructor calls per argument. You can move them into theinitializer_list
(std::vector<TS> vec {std::move(ts1), std::move(ts2)};
) but you cannot move them out, so at least one copy-constructor call is necessary. – Escutcheonstd::vector
, called à lavector<TS> vec( collect_references(ts1, ts2) );
. – Escutcheonvalue_type
. It should return a type with atemplate<class T> operator std::vector<T>() const
to deduce thevalue_type
for the vector to be initialized; it then can useemplace_back
and avoid any copy-ctor, if possible. Live example – Escutcheonstd::forward
appropriately ;) Fixed live example – Escutcheonauto function_name(parameter_list) -> return_type
is just another way to writereturn_type function_name(parameter_list)
(where you can use the parameter names in the return type); theTs&&...
here declares a function parameter pack, where each parameter is a "universal reference". I agree that it's complicated, but it should be invisible to the user ofcollect_references
. Each argument you pass is just forwarded to avec.emplace_back
call without any conversion, copying or moving. – Escutcheonstd::move
to store an rvalue reference in the reference collector; the argument will then be moved exactly once (into thevector
) and never copied. If you pass an lvalue, the argument will be copied exactly once (into thevector
). – Escutcheon