这将无法正常运行,因为list.begin()
具有type const T *
,并且无法从常量对象中移出。语言设计者可能这样做是为了允许初始化程序列表包含例如字符串常量,不宜从中进行移动。
但是,如果您处在知道初始化器列表包含右值表达式的情况下(或者您想强制用户编写这些值),那么有一种技巧可以使它起作用(我受Sumant的回答启发这样,但是解决方案比那个简单得多。您需要存储在初始化程序列表中的元素不是T
值,而是封装的值T&&
。这样,即使这些值本身是const
合格的,它们仍然可以检索可修改的右值。
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
};
现在initializer_list<T>
,您无需声明参数,而是声明initializer_list<rref_capture<T> >
参数。这是一个具体的示例,涉及一个std::unique_ptr<int>
智能指针向量,仅为其定义了移动语义(因此,这些对象本身永远无法存储在初始化列表中)。但是下面的初始化列表可以毫无问题地进行编译。
#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;
}
一个问题确实需要一个答案:如果初始化列表的元素应该是真实的prvalues(在示例中,它们是xvalues),那么该语言是否确保相应临时对象的生存期延长到使用它们的时间?坦白说,我认为标准的相关8.5节根本没有解决这个问题。但是,阅读1.9:10,似乎在所有情况下相关的全表达式都包含初始化列表的使用,因此我认为不存在悬挂右值引用的危险。
initializer_list<T>
是非 -const。喜欢,initializer_list<int>
是指int
物体。但是我认为这是一个缺陷-旨在使编译器可以在只读内存中静态分配列表。