Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 818
phön Avatar asked Jan 02 '23 20:01

phön


2 Answers

This is 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

So the requirements are

  1. in a return statement
  2. in a function
  3. with a class return type
  4. 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]))
  5. with the same type (ignoring cv-qualification) as the function return type

I would argue that requirements 1, 2, 3 and 5 are fulfilled, but requirement 4 is not. (a) is not the name of an object. Therefore for the given code copy-elision does not apply. Since the move-constructor has side-effects it also cannot be elided under the as-if rule.

Therefore gcc is right and visual studio (and clang) are wrong here.

like image 193
nwp Avatar answered Jan 05 '23 16:01

nwp


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.

like image 43
xskxzr Avatar answered Jan 05 '23 15:01

xskxzr