This won't work as stated, because list.begin()
has type const T *
, and there is no way you can move from a constant object. The language designers probably made that so in order to allow initializer lists to contain for instance string constants, from which it would be inappropriate to move.
However, if you are in a situation where you know that the initializer list contains rvalue expressions (or you want to force the user to write those) then there is a trick that will make it work (I was inspired by the answer by Sumant for this, but the solution is way simpler than that one). You need the elements stored in the initialiser list to be not T
values, but values that encapsulate T&&
. Then even if those values themselves are const
qualified, they can still retrieve a modifiable rvalue.
template<typename T>
class rref_capture
{
T* ptr;
public:
rref_capture(T&& x) : ptr(&x) {}
operator T&& () const { return std::move(*ptr); } // restitute rvalue ref
};
Now instead of declaring an initializer_list<T>
argument, you declare aninitializer_list<rref_capture<T> >
argument. Here is a concrete example, involving a vector of std::unique_ptr<int>
smart pointers, for which only move semantics is defined (so these objects themselves can never be stored in an initializer list); yet the initializer list below compiles without problem.
#include <memory>
#include <initializer_list>
class uptr_vec
{
typedef std::unique_ptr<int> uptr; // move only type
std::vector<uptr> data;
public:
uptr_vec(uptr_vec&& v) : data(std::move(v.data)) {}
uptr_vec(std::initializer_list<rref_capture<uptr> > l)
: data(l.begin(),l.end())
{}
uptr_vec& operator=(const uptr_vec&) = delete;
int operator[] (size_t index) const { return *data[index]; }
};
int main()
{
std::unique_ptr<int> a(new int(3)), b(new int(1)),c(new int(4));
uptr_vec v { std::move(a), std::move(b), std::move(c) };
std::cout << v[0] << "," << v[1] << "," << v[2] << std::endl;
}
One question does need an answer: if the elements of the initializer list should be true prvalues (in the example they are xvalues), does the language ensure that the lifetime of the corresponding temporaries extends to the point where they are used? Frankly, I don't think the relevant section 8.5 of the standard addresses this issue at all. However, reading 1.9:10, it would seem that the relevant full-expression in all cases encompasses the use of the initializer list, so I think there is no danger of dangling rvalue references.
initializer_list<T>
are non-const. Like,initializer_list<int>
refers toint
objects. But I think that is a defect - it is intended that compilers can statically allocate a list in read only memory. – Hotspur