Kyl*_*fel 6 c++ rvalue language-lawyer copy-assignment c++17
我和我的 C++ 同事遇到了一个奇怪的结构:
struct A { int i; };
void foo(A const& a);
int main() {
foo(A() = A{2}); // Legal
}
Run Code Online (Sandbox Code Playgroud)
这个A() = A{2}表达式完全把我们弄糊涂了,因为它似乎正在分配A{2}给一个临时的、默认构造的对象。但是在编译器资源管理器中看到它(https://gcc.godbolt.org/z/2LsfSk)。它似乎是一个法律声明(由 GCC 9 和 Clang 9 支持),如下声明:
struct A { int i; };
int main() {
A() = A{2};
auto a = A() = A{3};
}
Run Code Online (Sandbox Code Playgroud)
因此,在某些情况下,它似乎A()是一个左值。还是这里发生了其他事情?希望得到一些解释,最好是对 C++17 标准的引用。
更新:@Brian 发现这是赋值给右值的重复:为什么编译?. 但如果有人能在 C++ 标准中找到合适的参考,我将不胜感激。
A{}始终是每个[expr.type.conv]的右值
1个 甲简单型说明符或类型名称说明符 ,接着以带括号的可选表达式列表或通过一个支撑-INIT列表(初始化)构建体给出的初始值设定为指定的类型的值。如果该类型是推导的类类型的占位符,则它会被本小节剩余部分的类模板推导的重载决议选择的函数的返回类型替换。
2如果初始值设定项是带括号的单个表达式,则类型转换表达式等效于相应的强制转换表达式。否则,如果类型是cvvoid并且初始值设定项是()或{}(在包扩展之后,如果有的话),表达式是不执行初始化的指定类型的纯右值。 否则,表达式是指定类型的纯右值,其结果对象是用初始化程序直接初始化的。如果初始值设定项是带括号的可选表达式列表,则指定的类型不应是数组类型。
强调我的
这些作品在这里的原因并没有阻止它工作的标准。
对于内建类型等int有[expr.ass] / 1
赋值运算符 (=) 和复合赋值运算符都从右到左分组。所有都需要一个可修改的左值作为它们的左操作数;他们的结果是一个引用左操作数的左值。
所以这会阻止你做int{} = 42;. 但是,本节不适用于类。如果我们查看[class.copy.assign]没有任何说明需要左值,但第一段确实说明
用户声明的复制赋值运算符 X?::?operator= 是类 X 的非静态非模板成员函数,只有一个类型为 X、X&、const X&、volatile X& 或 const volatile X& 的参数
意思是
A{} = A{2};
Run Code Online (Sandbox Code Playgroud)
实际上是
A{}.operator=(A{2})
Run Code Online (Sandbox Code Playgroud)
在右值类对象上这样做是合法的,因为operator =您的类的默认值没有引用限定符来阻止它在右值上被调用。如果添加
A& operator=(const A& a) & { i = a.i; }
Run Code Online (Sandbox Code Playgroud)
toA而不是使用默认赋值运算符然后
A{} = A{2};
Run Code Online (Sandbox Code Playgroud)
将不再编译,因为operator=现在只适用于左值。