复制/移动省略是否允许使用格式正确的删除函数制作程序?

use*_*670 4 c++ constructor copy-elision c++17

请考虑以下代码:

#include <iostream>

struct Thing
{
    Thing(void)                       {std::cout << __PRETTY_FUNCTION__ << std::endl;}
    Thing(Thing const &)              = delete;
    Thing(Thing &&)                   = delete;
    Thing & operator =(Thing const &) = delete;
    Thing & operator =(Thing &&)      = delete;
};

int main()
{
    Thing thing{Thing{}};
}
Run Code Online (Sandbox Code Playgroud)

我希望Thing thing{Thing{}};声明意味着临时对象的建设Thing使用默认的构造函数和建设类thing的对象Thing使用仅仅用创建的临时对象作为参数转移构造类.我希望这个程序被认为是不正确的,因为它包含一个被删除的移动构造函数的调用,即使它可能被省略.标准的class.copy.elision部分似乎也要求这样做:

即使呼叫被省略,也必须可以访问所选的构造函数

通过简化值类别保证复制省略措辞似乎也不允许.

然而gcc 7.2(以及clang 4也是如此,但VS2017 仍然不支持保证复制省略)将编译此代码,只需移动构造函数调用即可.

在这种情况下哪种行为是正确的?

Sto*_*ica 9

它不会构建一个不正确的程序.它完全摆脱了对删除函数的引用.提案中的适当措辞如下:

[dcl.init] bullet 17.6

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

这个例子进一步强化了这一点.因为它表示整个表达式必须折叠成单个默认构造.

需要注意的是,当由于值类别而省略副本时,删除的函数永远不会使用,因此程序不是指它.

这是一个重要的区别,因为其他形式的复制省略仍然使用复制c'tor,如下所述:

[basic.def.odr]/3

...选择复制或移动类类型对象的构造函数是odr-used,即使调用实际上已被实现省略([class.copy] ...

[class.copy]描述了允许(但不是强制)复制省略的另一种形式.如果我们向您的班级证明:

Thing foo() {
    Thing t;
    return t; // Can be elided according to [class.copy.elision] still odr-used
}
Run Code Online (Sandbox Code Playgroud)

应该使程序格式不正确.海湾合作委员会按预期抱怨它.


顺便说一下.如果您认为在线编译器中的前一个示例是魔术师技巧,并且GCC抱怨因为它需要调用移动c'tor.看看我们提供定义时会发生什么.

  • @VTT:你没有传递物体.你正在传递prvalues,它们*显式*不是对象.[这就是保证elision工作的诀窍.prvalue还不是一个对象.它是对象的*初始化程序*.(/sf/ask/2663032361/#38043447)通过重新定义prvalues的含义,删除本来可以省略的操作. (2认同)