C++17中移动/复制构造函数/赋值运算符是“删除”还是“未声明”已经确定了吗?

9
我正在查看这个答案,涉及自动生成移动操作的规则?,希望现在已经有了很好的答案。
显示哪些构造函数/赋值运算符“未声明”,“默认”或“删除”,取决于类中声明了什么的幻灯片显示:

enter image description here

我将从这些幻灯片中获取这个内容,红色方框表示此行为已经过时。

当编译以下内容时:

#include <iostream>
struct X
{
  template<typename...T>
  X(T&&...) {
    std::cout << "Yay!\n";
  }

  ~X() {}
};

int main() {
  X x0;
  X x1{x0};
  X x2{std::move(x0)};
}

看起来它们已经“未声明”,因为它可以编译并输出“Yay!”三次(至少对我来说是好的)。但我想确认我是否可以依赖这种行为。

编辑

Frank 指出,如果还添加了一个拷贝构造函数,它仍然会输出“Yay!”三次,这是有趣的行为。进一步测试,如果添加移动构造函数,则只会输出“Yay!”两次。有人能解释这种行为吗?


@Frank,我不确定你的意思。如果构造函数未定义,则模板将匹配,否则它将不匹配。 - Adrian
@Adrian 我的意思是,你认为看到三次“Yay!”就意味着构造函数不存在是不正确的。这只是意味着它们没有被使用、存在或者不存在。在我的例子中,我明确地将复制构造函数添加回去,但它仍然会打印出三次“Yay!”。 - user4442671
2
你的模板生成普通构造函数,而不是复制或移动构造函数。复制/移动构造函数永远不能是模板。一个万能的模板构造函数也非常危险,它会匹配任何东西,并且几乎总是更好的匹配。 - rustyx
哦,我没注意到@Frank的评论。确实很有趣。 - Adrian
2
您的模板构造函数比复制构造函数更匹配(非const引用比const引用更匹配)。 - n. m.
显示剩余6条评论
1个回答

2
根据几乎C++17标准的N4659,它们仍被定义为默认值,但行为仍然被弃用。
对于复制构造函数,请参见[class.copy.ctor]/6
如果类定义没有显式声明复制构造函数,则会隐式声明一个非显式的复制构造函数。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为删除;否则,它被定义为默认值。如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则后一种情况被弃用 对于复制赋值,请参见[class.copy.assign]/2
如果类定义没有显式声明复制赋值运算符,那么会隐式地声明一个。如果类定义声明了移动构造函数或移动赋值运算符,那么隐式声明的复制赋值运算符将被定义为删除;否则,它会被默认定义。如果类有用户声明的复制构造函数或用户声明的析构函数,则后一种情况已被弃用。

不确定[class.copy.ctor]/1与我的示例有何关联。 - Adrian
@Adrian 抱歉,我没有听清你的意图。 - Oliv
@Adrian 你可以使用GCC9命令行选项-Wdeprecated-copy-Wdeprecated-copy-dtor或者只是-Wextra来获取警告信息(演示)。 - Oliv
本意是使用通用引用来绑定模板化构造函数,以代替复制/移动构造函数。 - Adrian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接