函数声明后= =的含义


241
class my_class
{
    ...
    my_class(my_class const &) = delete;
    ...
};

= delete在这种情况下是什么意思?

是否还有其他“修饰符”(= 0= delete除外)?


23
@Blindy,即将在C ++ 0x中成为标准。
康拉德·鲁道夫

1
我的立场是正确的,我错过了C ++ 0x功能。我当时以为是#definela Qt,其求值为0,然后声明了隐藏函数或其他内容。
Blindy

我想起了“ disable”关键字,意思相同或相似。我在想像吗?还是它们之间有细微的差别?
斯图尔特

Answers:


201

删除函数是C ++ 11的功能

现在可以直接表达“禁止复制”的常见用语:

class X {
    // ...
    X& operator=(const X&) = delete;  // Disallow copying
    X(const X&) = delete;
};

[...]

“删除”机制可用于任何功能。例如,我们可以消除这种不希望的转换:

struct Z {
    // ...

    Z(long long);     // can initialize with an long long         
    Z(long) = delete; // but not anything less
};

3
传统的“禁止复制”方法不是仅使copy-ctor和operator =“ private”吗?这会进一步,并指示编译器甚至不生成函数。如果它们既是私有的又是= delete,则禁止双重复制吗?
Reb.Cabin '17

8
@Reb,=delete即使在可以看到private方法的上下文中(即在类及其朋友中),也无法访问该方法。这样可以消除您在阅读代码时的不确定性。@Prasoon,第二个示例仍然只是删除构造函数-例如,很高兴看到已删除operator long ()
Toby Speight

2
@ Reb.Cabin Using = delete优于using private或其他类似的机制,因为通常您希望明显地声明禁用函数并考虑将其用于重载解决方案等,以便它可以尽早失败并向用户提供最明显的错误。任何涉及“隐藏”声明的解决方案都会减少这种影响。
Leushenko '19

1
是否有特殊的原因使复制构造函数公开并应用delete关键字。为什么不将构造函数设为私有并应用关键字呢?
恩·乔

81
  1. = 0意味着一个函数是纯虚函数,您不能从此类中实例化一个对象。您需要从中派生并实现此方法
  2. = delete表示编译器不会为您生成那些构造函数。AFAIK仅在复制构造函数和赋值运算符上允许这样做。但是我对即将到来的标准不太满意。

4
=delete语法还有其他用途。例如,您可以使用它来明确禁止调用可能发生的某种隐式转换。为此,您只需删除重载的函数。有关更多信息,请参见C ++ 0x上的Wikipedia页面。
2011年

我一发现就会立即这样做。猜到了该赶上c ++ 0X的时候了
mkaes 2011年

是的,C ++ 0x很烂。我等不及GCC 4.5+变得更常见了,所以我可以开始使用lambda了。
2011年

5
的描述= delete并不完全正确。= delete可以用于任何函数,在这种情况下,它将被显式标记为已删除,并且任何使用都会导致编译器错误。对于特殊的成员函数,这尤其也意味着它们不是由编译器为您生成的,但这仅是被删除的结果,而不是= delete真正的结果。
MicroVirus '16

28

摘录自The C ++ Programming Language [第4版]-Bjarne Stroustrup的书讨论了使用以下代码的真正目的=delete

3.3.4抑制操作

在层次结构中为类使用默认的复制或移动通常是一种灾难:仅提供指向基础的指针,我们根本不知道派生类具有哪些成员,因此我们不知道如何复制它们。因此,最好的做法通常是删除默认的复制和移动操作,即消除这两个操作的默认定义:

class Shape {
public:
  Shape(const Shape&) =delete; // no copy operations
  Shape& operator=(const Shape&) =delete;

  Shape(Shape&&) =delete; // no move operations
  Shape& operator=(Shape&&) =delete;
  ˜Shape();
    // ...
};

现在,编译器将捕获尝试复制Shape的尝试。

=delete机制是通用的,即可以用来抑制任何操作



5

我使用的编码标准在大多数类声明中都有以下内容。

//  coding standard: disallow when not used
T(void)                  = delete; // default ctor    (1)
~T(void)                 = delete; // default dtor    (2)
T(const T&)              = delete; // copy ctor       (3)
T(const T&&)             = delete; // move ctor       (4)
T& operator= (const T&)  = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)

如果使用这些6中的任何一个,则只需注释掉相应的行即可。

示例:FizzBu​​s类仅需要dtor,因此不要使用其他5。

//  coding standard: disallow when not used
FizzBuzz(void)                         = delete; // default ctor (1)
// ~FizzBuzz(void);                              // dtor         (2)
FizzBuzz(const FizzBuzz&)              = delete; // copy ctor    (3)
FizzBuzz& operator= (const FizzBuzz&)  = delete; // copy assig   (4)
FizzBuzz(const FizzBuzz&&)             = delete; // move ctor    (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign  (6)

我们在这里仅注释掉1,然后在其他地方(可能在编码标准建议的地方)安装它的实现。其他5个(共6个)不允许删除。

您还可以使用'= delete'来禁止不同大小的值的隐式提升...示例

// disallow implicit promotions 
template <class T> operator T(void)              = delete;
template <class T> Vuint64& operator=  (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;

3

= delete是C ++ 11中引入的功能。按照=delete它将不允许调用该函数。

详细地。

假设在一个班级。

Class ABC{
 Int d;
 Public:
  ABC& operator= (const ABC& obj) =delete
  {

  }
};

在调用此函数进行obj分配时,将不允许这样做。意味着赋值运算符将限制从一个对象复制到另一个对象。


2

新的C ++ 0x标准。请参阅N3242工作草案中的 8.4.3节


哇,那草案太过时了。这是最新的信息(截至2011
TonyK,

感谢并更新了链接。获取当前草稿非常有帮助。所引用的部分/内容即使在旧草案中也是正确的,因此我不赞成反对票。
dubnde 2011年

1

删除的函数隐式内联

(现有答案的附录)

...并且已删除的函数应是该函数的第一个声明(删除函数模板的显式特化除外-删除应在该特化的第一个声明处),这意味着您不能声明一个函数,然后再删除它,例如,按照其定义,仅适用于翻译单位。

引用[dcl.fct.def.delete] / 4

删除的函数是隐式内联的。(注:单定义规则([basic.def.odr])适用于已删除的定义。— 尾注 ]函数的已删除定义应为该函数的第一个声明,或者应为该函数模板的显式专门化,是该专业化的第一个声明。[示例:

struct sometype {
  sometype();
};
sometype::sometype() = delete;      // ill-formed; not first declaration

结束示例

带有已删除定义的主要功能模板可以专门化

尽管一般的经验法则是避免对功能模板进行专门化,因为专业化不参与重载解析的第一步,但是在某些情况下,可以使用它是有争议的。例如,当使用一个没有定义的非重载主函数模板来匹配所有类型时,它们不希望隐式地转换为通过转换进行匹配的重载;即,通过仅在未定义,未重载的主函数模板的显式特化中仅实现完全类型匹配,来隐式删除许多隐式转换匹配。

在删除C ++ 11的函数概念之前,可以通过简单地省略主函数模板的定义来做到这一点,但这会产生模糊的未定义参考错误,可以说主函数模板的作者没有任何语义意图(故意省略了) ?)。如果我们改为显式删除主功能模板,则在找不到合适的显式专业化的情况下出现的错误消息会变得更好,并且还表明有意省略/删除主功能模板的定义。

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t);

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    //use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}

但是,不删除上面的主要功能模板的定义,而是在没有明确的专业化匹配时产生模糊的未定义参考错误,可以删除主要模板定义:

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t) = delete;

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    use_only_explicit_specializations(str);
    /* error: call to deleted function 'use_only_explicit_specializations' 
       note: candidate function [with T = std::__1::basic_string<char>] has 
       been explicitly deleted
       void use_only_explicit_specializations(T t) = delete; */
}

产生一个更具可读性的错误消息,其中删除意图也清晰可见(其中未定义的参考错误可能导致开发人员认为这是一个未经考虑的错误)。

回到为什么我们要使用这种技术?同样,显式专业化对于隐式删除隐式转换可能很有用。

#include <cstdint>
#include <iostream>

void warning_at_best(int8_t num) { 
    std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}

template< typename T >
void only_for_signed(T t) = delete;

template<>
void only_for_signed<int8_t>(int8_t t) {
    std::cout << "UB safe! 1 byte, " << +t << "\n";
}

template<>
void only_for_signed<int16_t>(int16_t t) {
    std::cout << "UB safe! 2 bytes, " << +t << "\n";
}

int main()
{
    const int8_t a = 42;
    const uint8_t b = 255U;
    const int16_t c = 255;
    const float d = 200.F;

    warning_at_best(a); // 42
    warning_at_best(b); // implementation-defined behaviour, no diagnostic required
    warning_at_best(c); // narrowing, -Wconstant-conversion warning
    warning_at_best(d); // undefined behaviour!

    only_for_signed(a);
    only_for_signed(c);

    //only_for_signed(b);  
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = unsigned char] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */

    //only_for_signed(d);
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = float] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */
}

0

这是C ++ 0x标准中的新事物,您可以在其中删除继承的函数。


11
您可以删除任何功能。例如,void foo(int); template <class T> void foo(T) = delete;停止所有隐式转换。仅int接受类型为参数的参数,其他所有参数都将尝试实例化“已删除”函数。
UncleBens 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.