默认移动构造函数和引用成员

18

来自N3337的[12.8][11]:

非联合类X的隐式定义的复制/移动构造函数对其基类和成员执行逐成员的复制/移动。[注:忽略非静态数据成员的花括号或等号初始化项。参见12.6.2中的示例。——注释结束] 初始化顺序与用户定义构造函数中基类和成员的初始化顺序相同(见12.6.2)。设x为构造函数的参数,或对于移动构造函数,是引用该参数的xvalue。每个基类或非静态数据成员以适合其类型的方式进行复制/移动:

— 如果成员是数组,则每个元素均使用x的相应子对象进行直接初始化;

— 如果成员m具有rvalue引用类型T&&,则使用static_cast<T&&>(x.m)进行直接初始化;

— 否则,基类或成员将使用x的相应基类或成员进行直接初始化。

这实际上更像是一个澄清,但我在那个条款中没有看到任何关于左值引用成员的提及。由于它没有提到它们,因此默认情况下似乎它认为它们是隐式的逐成员移动的一部分,然而下面的例子不起作用:

int x = 5;
int& y = x;
int& z(std::move(y)); //error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'std::remove_reference<int&>::type {aka int}'

那么可以安全地假设默认的移动构造函数会区分成员是一个引用,并且只会执行

int& z = y;

没有调用 std::move


一个左值引用不能绑定到一个 xvalue,所以 int &z(std::move(y)); 不是合法的。int &z = y; 是合法的,它使得 z 成为当前命名为 xyint 对象的第三个名称。我不确定你的问题是什么? - M.M
“直接初始化”意味着对于成员变量 m,它的初始化就像是通过语句 T m(other.m); 进行的一样。 - M.M
我认为这与https://dev59.com/fVwX5IYBdhLWcg3w0iQc有关(可能已经解决)。 - Jonathan H
@Sh3ljohn,我在你提供的问题中没有看到答案。也许你可以直接指出相关的答案? - Walter
我认为答案是“是”。 - Walter
1个回答

15

这由类成员访问表达式的规范处理。关键部分是

x为构造函数的参数,或对于移动构造函数,是一个引用该参数的xvalue。

换句话说,对于

struct X { int x, &y; };

相当于

X::X(X&& other) : x(std::move(other).x), y(std::move(other).y) {}

这里的重要点是,当m指代非静态数据成员时,对于类成员访问表达式x.m的结果,如果m具有引用类型,则永远是一个左值;但如果x是一个右值且m具有非引用类型,则结果将是一个xvalue。 (见[expr.ref]/4.) 这确保了左值引用成员将被左值初始化。


3
y(std::move(other).y)y(other.y) 是相同的,对吗? - M.M
1
@M.M 对于引用类型的 y,是没错的。 - T.C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接