这个不会像陈述的那样起作用,因为
list.begin()
有类型
const T *
,而你没有办法从一个常量对象中移动。语言设计师可能是这样做的,以便允许初始化列表包含例如字符串常量,从中移动将是不合适的。
然而,如果您处于这样一种情况,在其中您知道初始化列表包含rvalue表达式(或者您想强制用户编写这些表达式),那么有一个技巧可以使其起作用(我受到了Sumant的答案的启发,但解决方案比那个简单得多)。你需要存储在初始化器列表中的元素不是
T
值,而是封装了
T&&
的值。即使这些值本身是
const
限定的,它们仍然可以检索可修改的rvalue。
template<typename T>
class rref_capture
{
T* ptr;
public:
rref_capture(T&& x) : ptr(&x) {}
operator T&& () const { return std::move(*ptr); }
};
现在,不再声明一个
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;
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后,似乎相关的
完整表达式在所有情况下都包括使用初始化列表,因此我认为不存在悬空rvalue引用的危险。
initializer_list<T>
引用的对象是非const的,比如initializer_list<int>
引用的是int
对象。但我认为这是一个缺陷 - 它原本是打算让编译器在只读内存中静态分配列表。 - Johannes Schaub - litb