delete/default 关键字是否将相应的方法标记为用户定义的?

Does delete/default keywords mark the corresponding method as user defined?

我有以下代码:

#include <iostream>

class B{
public:
    //this should all be generated by default but
    //throwing them in here just to be sure
    B() = default;
    B(const B& b) = default;
    B& operator=(const B& b) = default;
    B(B&& b) = default;
    B& operator=(B&& b) = default;
};

class A {
public:
    A(B x) : x_(x) {}
    A(const A& a) = delete;
    A& operator=(const A& a) = delete;
    //move operations should be generated by compiler?
private:
    B x_;
};

int main() {
    A a = A(B());
}

我希望它能够编译并使用它的移动构造函数创建一个 A,但是它失败了,并显示以下消息:

error: use of deleted function ‘A::A(const A&)’ A a = A(B()); note: declared here A(const A& a) = delete;

当然,添加移动操作并用默认关键字标记它们可以解决问题。我是否应该假设移动操作不是由编译器生成的,为什么呢? delete 关键字是否将方法标记为用户实现的,因此不会生成移动操作?为什么首选复制构造函数?我正在使用 gcc 进行编译。

如果您提供复制构造函数的实现/operator=,默认情况下不再生成移动操作。如果你想要它们,你需要明确地告诉你。

根据 cppreference:

If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:

  • there are no user-declared copy constructors;
  • there are no user-declared copy assignment operators;
  • there are no user-declared move assignment operators;
  • there are no user-declared destructors;

then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).

这是针对这种情况的有用图表:

attribution

= delete 你不只是说代码不存在,而是说如果代码试图调用它你应该得到一个编译错误。

更具体地说,deleted 成员不会简单地从可用选项集中删除:它们会被搜索,如果它们匹配,则会出现编译错误。换句话说,= delete 适用于匹配时暗示使用问题的签名,而不适用于您不想匹配的签名。

例如 deleted 成员 do indeed participate in overload resolution.

提供 = delete 与提供实现 完全相同,不同之处在于如果编译器最终生成将调用已删除函数的代码,那么您将进行编译错误。如果您提供了一个 deleted 复制构造函数,您仍然提供了一个用户定义的复制构造函数。

您应该注意的另一个微妙之处是,自动生成移动 constructor/assignment 取决于 "user defined copy constructor" 的存在以及该定义的严格含义。逻辑上不明显的东西(至少对我来说不是)是匹配复制构造函数签名的模板不是复制构造函数。换句话说:

struct Foo {
    template<typename T>
    Foo(const T& other) { ... }
};

模板可以匹配Foo(const Foo&)但是不被认为是用户定义的复制构造函数(不要寻找深层次的原因,这样是因为标准是这样说的)因此你仍然会得到隐式生成的移动构造函数。如果默认实现不正确,这将是严重问题的根源...