phö*_*hön 4 c++ language-lawyer copy-elision nrvo
我正在玩弄以下代码,并使用我的 Visual Studio 2017 应用程序和两个不同的在线编译器得到了不同的结果。在发布模式中,visual studio 在这两种情况下都省略了复制/移动,而两个在线编译器只是在没有括号的 return 语句的情况下才这样做。我的问题是:谁是对的,更重要的是基本规则是什么。(我知道您可以将括号与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();
}
Run Code Online (Sandbox Code Playgroud)
在线编译器1:http : //cpp.sh/75bux
在线编译器2: http //coliru.stacked-crooked.com/a/c266852b9e1712f3
发布模式下 Visual Studio 的输出是:
default constructor
default constructor
Run Code Online (Sandbox Code Playgroud)
在另外两个编译器中,输出是:
default constructor
default constructor
move constructor
Run Code Online (Sandbox Code Playgroud)
这是标准的相关引用:
这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以合并以消除多个副本):
(1.1) - 在具有类返回类型的函数中的 return 语句中,当表达式是非易失性自动对象的名称时(除了函数参数或由处理程序的异常声明引入的变量([ except.handle])) 与函数返回类型相同的类型(忽略 cv 限定),可以通过将自动对象直接构造到函数调用的返回对象中来省略复制/移动操作
所以要求是
我认为要求 1、2、3 和 5 已满足,但要求 4 未满足。(a)不是对象的名称。因此对于给定的代码复制省略不适用。由于移动构造函数有副作用,它也不能在 as-if 规则下被省略。
因此 gcc 是正确的,而 Visual Studio(和clang)在这里是错误的。
海湾合作委员会是对的。
这种复制/移动操作的省略称为复制省略,在以下情况下是允许的(可以组合起来消除多个副本):
在
return具有类返回类型的函数的语句中,当表达式是非易失性自动对象的名称(函数参数或由处理程序的异常声明引入的变量除外([except.handle]) ) 与函数返回类型具有相同的类型(忽略 cv 限定),可以通过将自动对象直接构造到函数调用的返回对象中来省略复制/移动操作...
语句中的括号表达式return不满足复制省略的条件。
事实上,在CWG 1597决议之前,语句中带括号的 id 表达式return甚至不能被视为执行移动的右值。
(这里是P1155“更多隐式移动”和P2266“更简单隐式移动”的作者。)
\n简短回答:这是一个 GCC 错误。现在已修复(GCC 11 和 GCC 12 之间)。
\n语言律师的答案可能是,从纸面上看,是否return (x);应该触发复制省略仍然不清楚。但实际上,我认为所有供应商现在都达成了共识。
[class.copy.elision]所说的是,“...当表达式是非易失性对象的名称时...”您可能会认为显然对象的名称不能包含括号 \xe2\x80\x94但是,另一方面,如果我们真的想说“字面意思是命名变量的标识符”,我们有一个非常完善的术语:我们会说“......当表达式是一个id-命名非易失性对象的表达式...”我们并没有这么说。
\n与此同时,当我们在 C++20 和 C++23 中清理“隐式移动”的措辞时,我们更加下心了。[expr.prim.id.unqual]/4现在(在 C++23 中)说:
\n\n\n在以下上下文中,id-表达式符合move-eligible:
\n\n
\n- (4.1) 如果id 表达式(可能带括号)
\nreturn是or语句的操作数co_return...- (4.2) 如果id 表达式(可能带括号)是throw 表达式的操作数...
\n
所以这意味着return (x);100% 肯定会经历“隐式移动”。由于供应商知道这一点,因此他们以正确的心态理解它也应该在可能的情况下进行复制省略。
| 归档时间: |
|
| 查看次数: |
729 次 |
| 最近记录: |