我正在使用以下代码愚弄,并使用我的visual studio 2017应用程序和两个不同的在线编译器得到了不同的结果。在发布模式下,visual studio在两种情况下都会忽略复制/移动,而两个在线编译器只是在没有表达的返回语句的情况下执行此操作。我的问题是:谁是正确的,更重要的是什么是承保规则。 (我知道您可以将括号与decltype(auto)
语法结合使用。但这不是当前的用例。)
示例代码:
#include <iostream>
#include <cstdio>
struct Foo
{
Foo() { std::cout << "default constructor" << std::endl; }
Foo(const Foo& rhs) { std::cout << "copy constructor" << std::endl; }
Foo(Foo&& rhs) { std::cout << "move constructor" << std::endl; }
Foo& operator=(const Foo& rhs) { std::cout << "copy assignment" << std::endl; return *this; }
Foo& operator=(Foo&& rhs) { std::cout << "move assignment" << std::endl; return *this; }
};
Foo foo_normal()
{
Foo a{};
return a;
}
Foo foo_parentheses()
{
Foo a{};
return (a);
}
int main()
{
auto a = foo_normal();
auto b = foo_parentheses();
std::getchar();
}
在线编译器1: http://cpp.sh/75bux
在线编译器2: http://coliru.stacked-crooked.com/a/c266852b9e1712f3
发布模式下visual studio的输出是:
default constructor
default constructor
在另外两个编译器中,输出是:
default constructor
default constructor
move constructor
答案 0 :(得分:4)
这是the relevant quote from the standard:
在下列情况下(可以合并以消除多份副本),允许复制/移动操作(称为复制省略)的省略:
(1.1) - 在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象的名称时(除了函数参数或由异常声明引入的变量) handler([except.handle]))具有与函数返回类型相同的类型(忽略cv-qualification),通过将自动对象直接构造到函数调用的返回对象中,可以省略复制/移动操作/ p>
所以要求是
我认为满足要求1,2,3和5,但要求4则不然。 (a)
不是对象的名称。因此,对于给定的代码,copy-elision不适用。由于移动构造函数具有副作用,因此也不能在as-if规则下省略。
因此gcc是对的,视觉工作室(和clang)在这里是错误的。
答案 1 :(得分:3)
根据[class.copy.elision] paragraph 1:
在以下情况下(允许合并以消除多份副本),允许复制/移动操作的省略,称为复制省略:
在具有类返回类型的函数中的
return
语句中,当表达式是非易失性自动对象的名称时(除了函数参数或由异常 - 声明引入的处理程序([except.handle]))具有相同类型的函数(忽略cv-qualification)作为函数返回类型,通过将自动对象直接构造到函数调用的返回对象中,可以省略复制/移动操作...
return
语句中的括号表达式不符合复制省略的标准。
事实上,在CWG 1597的解析之前,return
语句中带括号的id-expression甚至不能被视为执行移动的右值。