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

只谈情不闲聊 提交于 2020-01-04 03:29:05

问题


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

回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/48749440/c-nrvo-copy-elision-with-return-statement-in-parentheses

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!