是否允许 copy/move 省略以使使用已删除函数的程序格式正确?
Is copy/move elision allowed to make a program using deleted functions well-formed?
考虑以下代码:
#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{}};
}
我希望 Thing thing{Thing{}};
语句表示使用默认构造函数构造 Thing
class 的临时对象和构造 Thing
thing
对象 class 使用刚刚创建的临时对象作为参数的移动构造函数。我希望这个程序被认为是错误的,因为它包含对已删除移动构造函数的调用,即使它可能被省略。标准的 class.copy.elision 部分似乎也要求这样做:
the selected constructor must be accessible even if the call is elided
Wording for guaranteed copy elision through simplified value categories好像也不允许
但是 gcc 7.2(还有 clang 4,但不是 VS2017,它 still does not support guaranteed copy elision) will compile this code just fine 省略了移动构造函数调用。
在这种情况下哪种行为是正确的?
它不会生成格式错误的程序。它完全摆脱了对已删除函数的引用。提案中的适当措辞如下:
[dcl.init] 子弹 17.6
If the initializer expression is a prvalue and the cv-unqualified
version of the source type is the same class as the class of the
destination, the initializer expression is used to initialize the
destination object. [ Example: T x = T(T(T())); calls the T default
constructor to initialize x. ]
这个例子进一步加强了这一点。因为它表明整个表达式必须折叠成一个默认结构。
需要注意的是,当由于值类别而删除副本时,删除的函数永远不会被 ODR 使用,因此程序不会引用它。
这是一个重要的区别,因为 other 形式的复制省略仍然使用复制 c'tor,如下所述:
[basic.def.odr]/3
... A constructor selected to copy or move an object of class type is
odr-used even if the call is actually elided by the implementation
([class.copy] ...
[class.copy] 描述了另一种允许的(但不是强制的)复制省略形式。其中,如果我们用您的 class:
进行演示
Thing foo() {
Thing t;
return t; // Can be elided according to [class.copy.elision] still odr-used
}
应该使程序格式错误。 And GCC complains about it as expected.
顺便说一句。如果您认为在线编译器中的前一个示例是魔术师的把戏,并且 GCC 会抱怨,因为它需要 调用 move c'tor。 Have a look at what happens when we supply a definition.
考虑以下代码:
#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{}};
}
我希望 Thing thing{Thing{}};
语句表示使用默认构造函数构造 Thing
class 的临时对象和构造 Thing
thing
对象 class 使用刚刚创建的临时对象作为参数的移动构造函数。我希望这个程序被认为是错误的,因为它包含对已删除移动构造函数的调用,即使它可能被省略。标准的 class.copy.elision 部分似乎也要求这样做:
the selected constructor must be accessible even if the call is elided
Wording for guaranteed copy elision through simplified value categories好像也不允许
但是 gcc 7.2(还有 clang 4,但不是 VS2017,它 still does not support guaranteed copy elision) will compile this code just fine 省略了移动构造函数调用。
在这种情况下哪种行为是正确的?
它不会生成格式错误的程序。它完全摆脱了对已删除函数的引用。提案中的适当措辞如下:
[dcl.init] 子弹 17.6
If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object. [ Example: T x = T(T(T())); calls the T default constructor to initialize x. ]
这个例子进一步加强了这一点。因为它表明整个表达式必须折叠成一个默认结构。
需要注意的是,当由于值类别而删除副本时,删除的函数永远不会被 ODR 使用,因此程序不会引用它。
这是一个重要的区别,因为 other 形式的复制省略仍然使用复制 c'tor,如下所述:
[basic.def.odr]/3
... A constructor selected to copy or move an object of class type is odr-used even if the call is actually elided by the implementation ([class.copy] ...
[class.copy] 描述了另一种允许的(但不是强制的)复制省略形式。其中,如果我们用您的 class:
进行演示Thing foo() {
Thing t;
return t; // Can be elided according to [class.copy.elision] still odr-used
}
应该使程序格式错误。 And GCC complains about it as expected.
顺便说一句。如果您认为在线编译器中的前一个示例是魔术师的把戏,并且 GCC 会抱怨,因为它需要 调用 move c'tor。 Have a look at what happens when we supply a definition.