【问题标题】:Meaning of = delete after function declaration= 函数声明后删除的含义
【发布时间】:2011-07-27 16:46:41
【问题描述】:
class my_class
{
    ...
    my_class(my_class const &) = delete;
    ...
};

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

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

【问题讨论】:

  • 我已经纠正了,我错过了这个 C++0x 功能。我在想这是一个 #define 一个 Qt,它评估为 0,然后声明了一个隐藏函数或其他东西。
  • 我记得有一个“禁用”关键字,意思是相同或相似。我在想象吗?还是它们之间有细微的差别?

标签: c++ function c++11 declaration delete-operator


【解决方案1】:

删除函数是a C++11 feature:

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

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
};

【讨论】:

  • 传统的“禁止复制”方法不就是为了让copy-ctor和operator=“private”吗?这更进一步,并指示编译器甚至不生成函数。如果它们既是私有的又是 =delete,复制是双重禁止的吗?
  • @Reb, =delete 使该方法无法访问,即使从可以看到 private 方法的上下文中(即在类及其朋友中)也是如此。这消除了您在阅读代码时的任何不确定性。 @Prasoon,第二个示例仍然只是删除构造函数 - 例如,很高兴看到已删除的 operator long ()
  • @Reb.Cabin 使用= delete 比使用private 或其他类似机制更好,因为通常您希望明确声明禁止函数并考虑重载决议等,以便它可以尽早失败并为用户提供最清晰的错误。任何涉及“隐藏”声明的解决方案都会减少这种影响。
  • 是否有特殊原因使复制构造函数公开并应用删除关键字。为什么不将构造函数保留为私有并应用关键字?
  • 并非总是如此。不能删除派生的基类虚函数。
【解决方案2】:
  1. = 0 表示函数是纯虚函数,不能从此类实例化对象。您需要从中派生并实现此方法
  2. = delete 表示编译器不会为您生成这些构造函数。 AFAIK 仅允许在复制构造函数和赋值运算符上使用。但我不太擅长即将推出的标准。

【讨论】:

  • =delete 语法还有一些其他用途。例如,您可以使用它来明确禁止调用可能发生的某种隐式转换。为此,您只需删除重载的函数。查看 C++0x 上的 Wikipedia 页面了解更多信息。
  • 我会尽快找到一些。猜猜是时候赶上c++0X了
  • 是的,C++0x 摇滚。我等不及 GCC 4.5+ 更普遍了,所以我可以开始使用 lambdas。
  • = delete 的描述并不完全正确。 = delete 可用于任何函数,在这种情况下,它被显式标记为已删除,任何使用都会导致编译器错误。对于特殊的成员函数,这也特别意味着编译器不会为您生成它们,但这只是被删除的结果,而不是= delete 的真正含义。
【解决方案3】:

C++ 编程语言 [第 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机制是通用的,即可以用来抑制任何操作

【讨论】:

    【解决方案4】:

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

    既然似乎没有其他人回答这个问题,我应该提一下还有=default

    https://docs.microsoft.com/en-us/cpp/cpp/explicitly-defaulted-and-deleted-functions#explicitly-defaulted-functions

    【讨论】:

      【解决方案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;
      

      【讨论】:

      • 使用已删除的构造函数创建类的对象是非法的。
      • @Nikos - 不——您只需要提供一个构造函数。添加“ T() = delete; ”的示例阻止编译器添加(做最小)默认ctor,这偶尔有用,但仍然允许您添加(可能是有用的)ctor。跨度>
      【解决方案6】:

      = delete 是 C++11 中引入的一个特性。根据=delete,不允许调用该函数。

      详细。

      假设在一个班级里。

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

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

      【讨论】:

        【解决方案7】:

        已删除的函数是隐式内联的

        (现有答案的附录)

        ...并且删除的函数应该是函数的第一个声明(除了删除函数模板的显式特化 - 删除应该在特化的第一个声明处),这意味着你不能声明一个函数,然后再删除它,比如说,在翻译单元的本地定义中。

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

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

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

        ——结束示例)

        删除了定义的主函数模板可以特化

        尽管一般的经验法则是to avoid specializing function templates,因为特化不参与重载解决的第一步,但在某些情况下它可能有用。例如。当使用没有定义的 非重载 主函数模板来匹配所有不希望隐式转换为其他按转换匹配重载的类型时;即,通过仅在未定义、未重载的主函数模板的显式特化中实现精确类型匹配来隐式删除许多隐式转换匹配。

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

        #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; */
        }
        

        【讨论】:

          【解决方案8】:

          新的 C++0x 标准。请参阅N3242 working draft中的第 8.4.3 节

          【讨论】:

            【解决方案9】:

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

            【讨论】:

            • 您可以删除任何功能。例如 void foo(int); template &lt;class T&gt; void foo(T) = delete; 停止所有隐式转换。仅接受 int 类型的参数,所有其他参数都将尝试实例化“已删除”函数。
            猜你喜欢
            • 2022-11-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-08-03
            • 2021-06-25
            相关资源
            最近更新 更多