使用已删除的移动/复制ctor返回类型的临时对象

Pet*_*ica 6 c++ temporary copy-elision

考虑以下程序:

#include<iostream>
using namespace std;

struct S
{
    S() = default;
    S(const S& other) = delete;
    S(S&& other) = delete;
    int i;
};

S nakedBrace()
{
     return {}; // no S constructed here?
}

S typedBrace()
{
     return S{};
}

int main()
{
    // produce an observable effect.
    cout << nakedBrace().i << endl; // ok
    cout << typedBrace().i << endl; // error: deleted move ctor
}
Run Code Online (Sandbox Code Playgroud)

示例会话:

$ g++ -Wall -std=c++14 -o no-copy-ctor no-copy-ctor.cpp
no-copy-ctor.cpp: In function 'S typedBrace()':
no-copy-ctor.cpp:19:12: error: use of deleted function 'S::S(S&&)'
   return S{};
            ^
no-copy-ctor.cpp:8:5: note: declared here
     S(S&& other) = delete;
Run Code Online (Sandbox Code Playgroud)

gcc接受了我很惊讶nakedBrace().我认为概念上这两个函数是等价的:S构造并返回一个临时函数.可能会或可能不会执行复制省略,但仍必须按照标准(12.8/32)的要求访问移动或复制ctor(两者都在此处删除).

这是否意味着nakedBrace()永远不会构建一个S?或者它确实如此,但直接在带有大括号初始化的返回值中,因此在概念上不需要复制move/ctor?

Tar*_*ama 4

这是标准行为。

N4140 [stmt.return]/2:[...] 带有大括号初始化列表的 return 语句通过指定初始化列表中的复制列表初始化 (8.5.4) 来初始化要从函数返回的对象或引用。[...]

这意味着nakedBrace和执行的初始化typedBrace相当于:

S nakedBrace = {}; //calls default constructor
S typedBrace = S{}; //calls default, then copy constructor (likely elided)
Run Code Online (Sandbox Code Playgroud)