括号中带有 return 语句的 C++ nrvo/copy 省略

C++ nrvo/copy elision with return statement in parentheses

我在胡乱使用以下代码,使用我的 visual studio 2017 应用程序和两个不同的在线编译器得到了不同的结果。在发布模式下,visual studio 确实在两种情况下都省略了 copy/move,而两个在线编译器只是在未加括号的 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();
}

在线编译器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

这是the relevant quote from the standard:

This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

(1.1) - in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function parameter or a variable introduced by the exception-declaration of a handler ([except.handle])) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function call's return object

所以要求是

  1. 在 return 语句中
  2. 在函数中
  3. 具有 class return 类型
  4. 当表达式是非易失性自动对象的名称时(函数参数或由处理程序的异常声明引入的变量除外 ([except.handle]))
  5. 具有与函数相同的类型(忽略 cv 限定)return 类型

我认为满足要求 1、2、3 和 5,但不满足要求 4。 (a) 不是对象的名称。因此对于给定的代码复制省略不适用。由于 move-constructor 有副作用,它也不能在 as-if 规则下被省略。

因此 gcc 是正确的,visual studio(和 clang)在这里是错误的。

GCC 是对的。

根据[class.copy.elision] paragraph 1

This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function parameter or a variable introduced by the exception-declaration of a handler ([except.handle])) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function call's return object

  • ...

return 语句中的括号表达式不符合复制省略的条件。

事实上,在CWG 1597决议之前,return语句中带括号的id-expression甚至不能被视为执行移动的右值。