考虑以下计划:
#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
}
示例会话:
$ 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;
gcc接受nakedBrace()
让我感到很惊讶。我认为概念上这两个函数是等价的:构造并返回临时S
。可能会也可能不会执行复制省略,但仍必须按照标准(12.8 / 32)的要求访问移动或复制ctor(两者都在此处删除)。
这是否意味着nakedBrace()
从不构建S?或者它确实如此,但直接在带有大括号初始化的返回值中,因此在概念上不需要复制move / ctor?
答案 0 :(得分:4)
这是标准行为。
N4140 [stmt.return]/2:
[...]带有braced-init-list的return语句初始化对象 或者引用从指定的初始值设定项通过copy-list-initialization(8.5.4)从函数返回 名单。 [...]
这意味着nakedBrace
和typedBrace
执行的初始化与以下内容相同:
S nakedBrace = {}; //calls default constructor
S typedBrace = S{}; //calls default, then copy constructor (likely elided)
答案 1 :(得分:1)
[stmt.return] / 2 ...表达式为非void类型的return语句只能在返回值的函数中使用;表达式的值返回给函数的调用者。表达式的值隐式转换为它出现的函数的返回类型。 return语句可能涉及构造和复制或移动临时对象(12.2)...带有 braced-init-list 的return语句初始化要从函数返回的对象或引用从指定的初始化列表中复制列表初始化(8.5.4)。
[class.temporary] / 1 班级类型的临时工作是在各种语境中创建的:...返回prvalue(6.6.3)......
所以是的,据我所知,存在语义差异。 typedBrace
计算表达式S{}
,它生成类型为S
的prvalue,然后尝试从该表达式复制构造其返回值。 nakedBrace
改为直接从braced-init-list构造其返回值。
这与S s{};
(作品)vs S s = S{};
(不起作用)的情况相同,只是在一定程度上间接地模糊了。