类似乎可以使用用户定义的析构函数移动

我想弄清楚为什么这段代码不能编译?我已经创建了用户定义的析构函数,因此不应创建移动构造函数和移动赋值运算符。那么为什么复杂化失败,说我的班级不符合要求?

#include <iostream>
#include <concepts>
#include <type_traits>

template<class T>  requires (!std::movable<T>)
struct Singleton
{

};

struct Widget 
{
  ~Widget() = default;
};

int main()
{
  auto x = Singleton<Widget>{};
}

编辑。这个版本也是如此。

#include <iostream>
#include <concepts>
#include <type_traits>

extern void fun();

template<class T>  requires (!std::movable<T>)
struct Singleton
{

};

struct Widget 
{
  ~Widget() 
  {
    fun();
  } 
};

int main()
{
  auto x = Singleton<Widget>{};
}

错误信息

:23:28: 注意: 不满足约束: 替换“模板需要 !(movable) > struct Singleton [with T = Widget]”: :23:28: 从这里需要 :8:8: 需要的约束'模板需要 !(movable) struct Singleton' :7:30: 注意:表达式 '!(movable) [with T = Widget]' 评估为 'false' 7 | 模板需要 (!std::movable)

在 Godbolt 上编译使用的 gcc (trunk)。我使用的唯一编译标志是 -std=c++20

https://godbolt.org/z/sb535b1qv

回答

在这两个示例中都没有移动构造函数。但这不是std::movable检查的内容。它推迟了您所测试的检查std::move_constructible。该概念仅检查对象是否可以从右值直接初始化和复制初始化。

从 C++98 时代起,复制构造函数就能够从右值进行初始化。它仍然适用于添加移动操作。这就是为什么特性得到满足的原因。Widget a; Widget b = std::move(a);即使没有移动构造函数,它仍然有效,因为编译器提供的复制构造函数使这成为可能。

编译器不生成移动构造函数是为了在迁移到 C++11 时保持旧代码正常工作而做出的设计决定。这样的代码将遵守三规则,因此将旧副本交换为编译器生成的移动被认为是有风险的。但是旧代码仍然可以从右值初始化对象,所以复制构造函数保留了它的旧函数。


值得注意的是,该特性在未来可能会如人们所期望的那样发挥作用。此处定义的默认复制构造函数是不推荐使用的功能:

6如果类定义未显式声明复制构造函数,则隐式声明非显式构造函数。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为已删除;否则,它被定义为默认值 ([dcl.fct.def])。如果类具有用户声明的复制赋值运算符或用户声明的析构函数([depr.impldec]),则不推荐使用后一种情况


以上是类似乎可以使用用户定义的析构函数移动的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>