是否允许复制/移动省略使使用已删除函数的程序格式正确?
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
类的临时对象thing
并使用移动构造函数构造Thing
类的对象,并刚刚创建的临时对象作为参数。我希望这个程序被认为是格式错误的,因为它包含对已删除移动构造函数的调用,即使它可能被省略。标准的class.copy.elision部分似乎也要求这样做:
即使省略了调用,所选构造函数也必须可访问
通过简化的价值类别保证副本省略的措辞似乎也不允许这样做。
然而,gcc 7.2(以及clang 4,但不是仍然不支持保证复制的VS2017)将编译此代码,只是很好地省略了移动构造函数调用。
在这种情况下,哪种行为是正确的?
它不会生成格式错误的程序。它完全摆脱了对已删除函数的引用。提案中的适当措辞如下:
[dcl.init] 项目符号 17.6
如果初始值设定项表达式是 prvalue 且 cv 不限定 源类型的版本与 目标,初始值设定项表达式用于初始化 目标对象。[ 示例:T x = T(T(T(T()))); 调用 T 默认值 用于初始化 x 的构造函数。
这个例子进一步加强了这一点。因为它指示整个表达式必须折叠成单个默认构造。
需要注意的是,由于值类别而省略副本时,永远不会使用已删除的函数,因此程序不会引用它。
这是一个重要的区别,因为另一种形式的复制省略仍然使用复制 c'tor,如下所述:
[basic.def.odr]/3
。选择用于复制或移动类类型的对象的构造函数是 ODR 使用,即使调用实际上被实现省略了 ([类.副本] ...
[class.copy] 描述了另一种形式的允许(但不是强制性的)复制省略。如果我们与您的班级一起演示:
Thing foo() {
Thing t;
return t; // Can be elided according to [class.copy.elision] still odr-used
}
应该使程序格式不正确。海湾合作委员会如预期的那样抱怨它。
顺便说一下。如果你认为在线编译器中的前面的例子是一个魔术师的把戏,而GCC抱怨,因为它需要调用移动c'tor。看看当我们提供定义时会发生什么。
- 为什么我的双向链表删除函数会删除多个节点?
- unique_ptr实现接口时对已删除函数的引用
- 可视代码 删除函数括号内的空格
- 从尝试引用已删除函数的矢量 C++ 中删除对象
- 试图引用已删除函数数组的相等运算符
- 双链表堆栈删除函数不起作用
- 是否需要删除函数中未使用的新结构?
- C++引用已删除函数错误
- 删除函数环境中C++输入 R 对象
- 删除函数中的对象C++
- 在 C++ 中使用删除函数的不同方式
- C++错误:C2280 - 引用已删除函数的编译器错误?
- C++,删除函数/迭代进程中定义的动态数组
- 删除函数 c++ 的读取访问冲突异常
- 如何对此自定义C 列表类实现删除函数
- 双向链表问题(特别是复制构造函数和删除函数)
- 引用 std::atomic <bool>的已删除函数错误
- 在删除函数自由度的指针后,为什么我会得到核心转储
- C++删除函数
- 返回 C++11 中已删除函数的类型