为啥在特殊成员函数中将 r 值绑定到 const 左值引用是非法的?
Posted
技术标签:
【中文标题】为啥在特殊成员函数中将 r 值绑定到 const 左值引用是非法的?【英文标题】:Why is it illegal to bind an r-value to a const l-value reference in special member functions?为什么在特殊成员函数中将 r 值绑定到 const 左值引用是非法的? 【发布时间】:2020-12-03 00:31:24 【问题描述】:对于函数参数,可以将一个右值绑定到一个左值常量引用。 但是,这似乎不适用于特殊的成员函数,例如 C++11 和 C++14 中的复制构造函数和复制赋值运算符。有这样做的动机吗?
使用 C++17 时,可以从 r 值复制构造,但不能复制赋值。 是否有动机为什么这里只更改了复制构造函数的行为?
所有这些都在以下示例中得到了展示:
struct B
B() = default;
B(B const&) = default;
B(B&&) = delete;
B& operator=(B const&) = default;
B& operator=(B&&) = delete;
;
void bar(B const &)
int main()
bar(B); // does work
B(B); // only works in C++17
B b;
b = B; // doesn't work
【问题讨论】:
【参考方案1】:B(B);
从 C++17 开始工作,因为 mandatory copy elision,完全省略了移动构造;临时对象直接由默认构造函数初始化。
(强调我的)
在以下情况下,编译器必须省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数具有可观察到的副作用。对象直接构建到存储中,否则它们将被复制/移动到。 复制/移动构造函数不需要存在或可访问:
...
在初始化一个对象时,当初始化表达式是一个相同类类型的prvalue(忽略cv-qualification)时 变量类型:
T x = T(T(f())); // only one call to default constructor of T, to initialize x
注意:上述规则未指定优化:prvalues 和 temporaries 的 C++17 核心语言规范与早期 C++ 修订版的基本不同:不再有临时复制/移动从。描述 C++17 机制的另一种方式是“未实现的值传递”:纯右值被返回和使用,而无需实现临时值。
在 C++17 之前,这是一种优化,B(B);
格式不正确。
这是一个优化:即使它发生并且没有调用 copy
/move (since C++11)
构造函数,它仍然必须存在并且可以访问(好像根本没有进行优化),否则程序是错误的
bar(B);
有效,因为bar
只有一个重载,它采用左值引用到const
,右值可以绑定到它;这在 C++11 之后没有改变。
b = B;
不起作用,因为选择了重载的移动赋值运算符;即使它被明确标记为delete
,它仍然参与重载决议[1]。对于bar
,如果您添加一个以右值引用为的重载
void bar(B&&)=delete;
它会被选中并导致程序格式错误。
[1] 请注意,deleted implicitly declared move constructors 并非如此,它们会被重载决议忽略。 (C++14 起)
overload resolution 忽略已删除的隐式声明的移动构造函数(否则会阻止从右值进行复制初始化)。
【讨论】:
我要补充一点,其他示例不起作用,因为delete
是在重载决议选择正确的函数之后应用的,即使是从已删除的函数中。添加void bar(B&&)=delete;
也会导致编译错误。
还有更多。 “重载决议忽略定义为已删除的默认移动构造函数”(class.copy.ctor)。关于移动分配也是如此。但它在这里不适用,因为移动 ctor 不是默认的(即显式删除)。但是如果你有struct C : B
,你可以从any 右值引用中复制构造和复制赋值C
(例如C c; C cc = std::move(c);
。我相信C++14也是如此。跨度>
B(B);
不是初始化。为什么在初始化中引用关于复制省略的“规则”?
@LanguageLawyer 为什么不是初始化?不是在初始化一个临时对象吗?而且,应该是什么?
引用说...当初始化表达式是prvalue ...时,AFAIK这个术语(它甚至是一个术语吗?) “初始化表达式”仅在引用中的 T x = T(T(f()));
等声明的上下文中使用 (timsong-cpp.github.io/cppwp/n4659/dcl.init#17)。 B(B);
是 expression-statement,而不是 declaration。以上是关于为啥在特殊成员函数中将 r 值绑定到 const 左值引用是非法的?的主要内容,如果未能解决你的问题,请参考以下文章
在 C++ 11 中将非 const 左值引用绑定到右值是不是有效?(修改)
Qt 如何在 QtConcurrent 中将指针绑定到非静态成员函数?