在c ++草案n4606中有一段关于保证副本省略的段落[dcl.init] 17.6:
- 如果目标类型是(可能是cv限定的)类类型:
- 如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类相同,则初始化表达式用于初始化目标对象。 [示例:
T x = T(T(T()));
调用T
默认构造函数来初始化x
。 - 结束示例]- [...]
还有Q&A谈论它是如何运作的。
对我自己的理解,我引用的规则保证当初始化表达式是prvalue并且源类型的cv-nonqualified版本与目标类相同时,不应该涉及ctors。所以不需要检查复制的存在或移动ctor,这使得以下代码在C ++ 17中是合法的:
struct A {
A() {}
A(A const &) = delete;
A(A &&) = delete;
};
A f() { return A(); } // it's illegal in C++14, and suppose to be legal in C++17
但是,令我发疯的是我无法在c ++草案n4606的列表初始化部分找到类似的规则。我发现的是([dcl.init.list] 3.6)
[...]
- 否则,如果
T
是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数。如果转换任何参数需要缩小转换(见下文),则程序格式错误。 [...]
由于列表初始化的优先级高于我引用的第一个规则,因此当初始化程序是初始化程序列表时,我们应该考虑列表初始化部分中的规则。我们可以看到,在列表初始化类类型T
时会考虑构造函数。所以,继续上一个例子,将
A ff() { return {A()}; }
在C ++ 17中合法吗?并且有人可以找到标准草案指定保证副本省略在列表初始化中如何工作的地方吗?
答案 0 :(得分:5)
Guaranteed elision通过重新定义prvalue表达式来表示“将初始化对象”。他们不再建造临时工;临时数据由某些使用的prvalue表达式构成。
请注意经常使用上面的“表达”一词。我指出这一点是因为一个非常重要的事实:braced-init-list 不是表达式。标准非常明确。它不是表达式,只有表达式可以是prvalues。
确实,请考虑关于省略的标准部分:
在下列情况下,允许复制/移动操作的省略,称为复制省略:
- 在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象的名称时...
- ...
- 当一个尚未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-unqualified类型的类对象时
这些都涉及表达式(临时类对象是表达式)。 Braced-init-lists不是表达式。
因此,如果您发出return {anything};
,则anything
的返回值的构造将不会被省略,无论anything
是什么。当然,按照标准;编译器可能因错误而有所不同。
现在要说的是,如果你的prvalue表达式与返回值的类型相同,那么你不太可能想要return {prvalue};
而不是return prvalue;
。如果表达式是不同的类型,那么无论如何它都不符合elision的条件。