“隐式删除”构造函数 =delete 还是根本没有声明?[复制]

Is "implicitly deleted" constructor =delete or not declared at all? [duplicate]

提问人:CPPL 提问时间:6/6/2022 更新时间:6/6/2022 访问量:977

问:

给定以下玩具代码:

class X
{
public:
    X() { }
    X(const X&) { }
    //X(X&&) = delete;
};

int main()
{
    X x;
    X y = std::move(x);
}

我知道在这种情况下它被隐式删除,因为作为用户声明的构造函数存在。但我对这里术语“隐式删除”的含义有点困惑:如果我们取消注释,代码的行为会有所不同,这意味着式删除和“显式”删除之间存在差异。X::X(X&&)X(const X&)X(X&&) = delete;

在我的脑海中,有两种不同的理解:

  • “隐式删除”意味着编译器生成一些类似于的代码(也就是说,编译器知道有并且它知道它被删除了),但生成的代码与尝试调用时的不同之处在于,编译器选择而不是报告错误。(已取消注释,编译器将不会选择并报告错误)X(X&&) = delete;X(X&&)X(X&&)X(X&&) = delete;X y = std::move(x);X::X(X&&)X(const X&)X(X&&) = delete;X(const X&)

  • “隐式删除”意味着根本没有在类中声明,换句话说,编译器没有任何关于 的信息,因此直接匹配到 。X(X&&)X(X&&)X y = std::move(x);X(const X&)

请问我的哪一种理解是正确的?


我的猜测是前者应该是正确的。因为如果我们按如下方式更改上面的代码:

class X
{
public:
    X() { }
    //X(const X&) { }
    X(X&&) {}
};

int main()
{
    X x;
    X y = x;
}

我们得到一个错误,这意味着编译器知道它何时被隐式删除。'X::X(const X &)': attempting to reference a deleted functionX(const X &)

然而,对我来说,我的后一种理解似乎是一种更直接的完成工作的方式。所以我想知道我们为什么要设计“隐式删除”的概念(这也让我有点不一致的感觉,因为“隐式删除”需要与“显式删除”不同的行为)

C++ 删除函数

评论

0赞 François Andrieux 6/6/2022
您需要考虑将移动语义添加到语言的上下文中。他们必须确保现有代码能够继续按预期工作,甚至在任何人想到移动语义之前就编写了代码。我相信理由是,具有复制构造函数而没有显式移动构造函数的旧代码应该像以前一样继续工作,即使在通常使用移动构造的情况下,也可以使用复制构造函数。通过添加显式删除的移动构造函数,可以证明您的类不是旧代码,并且您有意不希望允许移动构造。
1赞 Eljay 6/6/2022
隐式删除的真正含义是“隐式不由编译器合成”。它与 不同,因为签名可用于匹配重载解析,如果匹配,则将失败,因为它已被显式删除。我希望有一种方法可以用代码(而不是注释)编写,但不要让它成为重载解决方案。X(X&&) = delete;X(X&&) = delete(intentional);

答:

6赞 Enlico 6/6/2022 #1

当您取消注释时,您正在做的是将该 ctor 声明为已删除。例如,这意味着它确实参与了重载解析,在 的情况下,它赢了,但实际上不能使用,因为它缺少主体。X(X&&) = delete;X y = std::move(x);

请注意,这并不特别适用于(特殊或非)成员函数,而是适用于一般函数:

#include<iostream>

// this is akin to defining both copy and move ctor
auto f(int) { std::cout << "int" << std::endl;}
auto f(double) { std::cout << "double" << std::endl;}

// this is akin to defining copy ctor and deleting move ctor
auto g(int) { std::cout << "int" << std::endl;}
auto g(double) = delete;

// this is akin to defining only copy ctor
auto h(int) { std::cout << "int" << std::endl;}
// auto h(double) is not even delcared

int main() {
    f(1);
    f(1.2);
    g(1);
    //g(1.2); // compile time error
    h(1);
    h(1.2); // round error
}

特定于特殊成员函数的是声明/定义/ing/ing/不写入一个函数如何影响另一个函数。defaultdelete

本页解释了所有内容,但需要非常仔细的阅读。

这里有一个棘手的点(我的粗体):

删除了隐式声明的移动构造函数

如果满足以下任一条件,则类的隐式声明或默认移动构造函数定义为已删除T

  • T 具有无法移动的非静态数据成员(已删除、无法访问或不明确的移动构造函数);
  • [...]

[...]

以上是什么意思?

下面是一个示例:

// copyable but not movable
struct CNM {
    CNM() {};
    CNM(CNM const&) = default;
    CNM(CNM&&) = delete;
};

struct W {
    W() {}
    W(W&&) = default; // defaulted... but actually deleted!
    CNM nm;
};

W w1;
W w2{std::move(w1)}; // compile time error

评论

3赞 Revolver_Ocelot 6/6/2022
前现代 C++ 等同于删除,是在声明函数而不定义函数的情况下。
1赞 Matt 6/6/2022
比较与不存在是说明这个问题的好方法!+1g(double) = deleteh(double)
0赞 Enlico 6/6/2022
@Matt,在这种情况下,我会更加强调它。