保证副本省略如何在C++ 1z中的列表初始化中工作?

Car*_*sel 9 c++ copy-elision list-initialization c++17

在c ++草案n4606 [dcl.init] 17.6中有一段关于保证副本省略的段落:

  • 如果目标类型是(可能是cv限定的)类类型:
    • 如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类相同,则初始化表达式用于初始化目标对象.[ 示例:T x = T(T(T()));调用T默认构造函数进行初始化x.- 结束例子 ]
    • [...]

还有关于它如何运作的问答报告.

对我自己的理解,我引用的规则保证当初始化表达式是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
Run Code Online (Sandbox Code Playgroud)

然而,令我发疯的是我在c ++ draft n4606的list-initialization部分找不到类似的规则.我发现的是([dcl.init.list] 3.6)

[...]

  • 否则,如果T是类类型,则考虑构造函数.枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数.如果转换任何参数需要缩小转换(见下文),则程序格式错误.[...]

由于列表初始化的优先级高于我引用的第一个规则,因此当初始化程序是初始化程序列表时,我们应该考虑列表初始化部分中的规则.我们可以看到,在列表初始化类类型时会考虑构造函数T.所以,继续前面的例子,将

A ff() { return {A()}; }
Run Code Online (Sandbox Code Playgroud)

在C++ 17中合法吗?有人可以找到标准草案指定保证副本省略在列表初始化中如何工作的地方吗?

Nic*_*las 5

保证省略通过重新定义prvalue表达式来表示"将初始化对象".他们不再建造临时工; 相反,临时数由prvalue表达式的某些使用构成.

请注意经常使用上面的"表达"一词.我指出这是因为一个非常重要的事实:braced-init-list 不是表达式.标准非常明确.它不是表达式,只有表达式可以是prvalues.

确实,请考虑关于省略的标准部分:

在下列情况下,允许复制/移动操作的省略,称为复制省略:

  • 在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象的名称时...
  • ...
  • 当一个尚未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-unqualified类型的类对象时

这些都涉及表达式(临时类对象是表达式).Braced-init-lists不是表达式.

因此,如果你发行,不管是什么,都不会省略return {anything};返回值的构造.当然,按照标准; 编译器可能因错误而有所不同.anythinganything

既然如此,如果你有一个与返回值相同类型的prvalue表达式,你极不可能想要键入return {prvalue};而不是只是return prvalue;.如果表达式是不同的类型,那么无论如何它都不符合elision的条件.