c++language-lawyercopy-elisionnrvo

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


i was fooling around with the following code and got different results using my visual studio 2017 application and two different online compilers. in release mode visual studio does elide the copy/move in both cases, while the two online compilers just do it in case of the unparenthesized return statement. my question is: who is right and more importantly what are the underlaying rules. (i know you can use the parentheses in conjunction with the decltype(auto)syntax. but this is not the current usecase).

example code:

#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();
}

online compiler 1: http://cpp.sh/75bux

online compiler 2: http://coliru.stacked-crooked.com/a/c266852b9e1712f3

the output for visual studio in release mode is:

default constructor
default constructor

in the two other compilers the output is:

default constructor
default constructor
move constructor

Solution

  • GCC is right.

    According to [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

    • ...

    Parenthesized expression in return statement does not meet the criteria for copy elision.

    In fact, until the resolution of CWG 1597, parenthesized id-expression in return statement cannot even be considered as an rvalue to perform a move.