函数声明后=删除的含义

Pat*_*efe 224 c++ declaration function delete-operator c++11

class my_class
{
    ...
    my_class(my_class const &) = delete;
    ...
};
Run Code Online (Sandbox Code Playgroud)

= delete在这种情况下意味着什么?

还有其他"修饰符"(除了= 0= delete)吗?

Pra*_*rav 187

删除函数是C++ 11的一项功能:

现在可以直接表达"禁止复制"的常用习语:

class X {
    // ...
    X& operator=(const X&) = delete;  // Disallow copying
    X(const X&) = delete;
};
Run Code Online (Sandbox Code Playgroud)

[...]

"删除"机制可用于任何功能.例如,我们可以消除不需要的转换,如下所示:

struct Z {
    // ...

    Z(long long);     // can initialize with an long long         
    Z(long) = delete; // but not anything less
};
Run Code Online (Sandbox Code Playgroud)

  • 传统的“禁止复制”方法不就是为了让copy-ctor和operator=“私有”吗?这更进一步,并指示编译器甚至不生成函数。如果它们既是私有的又是 =delete,那么复制是双重禁止的吗? (6认同)
  • @Reb,`= delete`使得该方法即使从可以看到`private`方法的上下文(即在类及其朋友中)也无法访问.这样可以在您阅读代码时消除任何不确定性.@Prasoon,第二个例子仍然只是删除构造函数 - 例如,看到删除的`运算符long()`会很高兴. (6认同)
  • @Reb.Cabin 使用 `=delete` 比使用 `private` 或其他类似机制更好,因为通常你*希望*被禁止的函数被明显声明并被考虑用于重载解析等,以便它可以尽早失败并将最清晰的错误提供给用户。任何涉及“隐藏”声明的解决方案都会减少这种影响。 (3认同)
  • 是否有特殊原因将复制构造函数公开并应用删除关键字。为什么不将构造函数保留为私有并应用关键字? (2认同)

mka*_*aes 77

  1. = 0表示函数是纯虚拟的,您无法从此类实例化对象.您需要从中派生并实现此方法
  2. = delete意味着编译器不会为您生成这些构造函数.AFAIK只允许复制构造函数和赋值运算符.但我对即将推出的标准不太了解.

  • `= delete`的描述并不完全正确.`= delete`可用于任何函数,在这种情况下,它被显式标记为已删除,任何使用都会导致编译器错误.对于特殊的成员函数,这也特别意味着编译器不会为它们生成它们,但这只是被删除的结果,而不是真正的`= delete`. (5认同)
  • `= delete`语法还有一些其他用途.例如,您可以使用它明确禁止可能在调用时发生的某种隐式转换.为此,您只需删除重载的功能.有关详细信息,请查看C++ 0x上的Wikipedia页面. (4认同)

Sau*_*ahu 24

摘自"C++编程语言[第4版] - Bjarne Stroustrup"一书讲述了使用背后的真正目的=delete:

使用层次结构中的类的默认副本或移动通常是一个灾难:只给出指向基类的指针,我们根本不知道派生类有哪些成员(§3.2.2),所以我们无法知道如何复制它们.因此,最好的做法通常是删除默认副本和移动操作,即消除这两个操作的默认定义:

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();
    // ...
};
Run Code Online (Sandbox Code Playgroud)

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

=delete机制是通用的,也就是说,它可以用于抑制任何操作


Kyl*_*ney 8

还有其他"修饰符"(除了= 0= delete)吗?

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

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


278*_*528 8

我使用过的编码标准对于大多数类声明都有以下​​内容。

//  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)
Run Code Online (Sandbox Code Playgroud)

如果您使用这 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)
Run Code Online (Sandbox Code Playgroud)

我们在这里只注释掉 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;
Run Code Online (Sandbox Code Playgroud)


dfr*_*fri 6

删除的函数是隐式内联的

\n\n

(现有答案的附录)

\n\n

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

\n\n

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

\n\n
\n

删除的函数是隐式内联的。(注:单一定义规则 ( [basic.def.odr] )\n 适用于已删除的定义。 \xe2\x80\x94尾注] 函数的已删除定义应为第一个声明函数的\n 或对于函数模板的显式特化,该特化的第一个\n 声明。[ 示例:

\n\n
struct sometype {\n  sometype();\n};\nsometype::sometype() = delete;      // ill-formed; not first declaration\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\x94结束示例

\n
\n\n

可以专门化具有已删除定义的主函数模板

\n\n

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

\n\n

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

\n\n
#include <iostream>\n#include <string>\n\ntemplate< typename T >\nvoid use_only_explicit_specializations(T t);\n\ntemplate<>\nvoid use_only_explicit_specializations<int>(int t) {\n    std::cout << "int: " << t;\n}\n\nint main()\n{\n    const int num = 42;\n    const std::string str = "foo";\n    use_only_explicit_specializations(num);  // int: 42\n    //use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,不是简单地省略上面主函数模板的定义,当没有显式专业化匹配时产生模糊的未定义引用错误,而是可以删除主模板定义:

\n\n
#include <iostream>\n#include <string>\n\ntemplate< typename T >\nvoid use_only_explicit_specializations(T t) = delete;\n\ntemplate<>\nvoid use_only_explicit_specializations<int>(int t) {\n    std::cout << "int: " << t;\n}\n\nint main()\n{\n    const int num = 42;\n    const std::string str = "foo";\n    use_only_explicit_specializations(num);  // int: 42\n    use_only_explicit_specializations(str);\n    /* error: call to deleted function \'use_only_explicit_specializations\' \n       note: candidate function [with T = std::__1::basic_string<char>] has \n       been explicitly deleted\n       void use_only_explicit_specializations(T t) = delete; */\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n
#include <cstdint>\n#include <iostream>\n\nvoid warning_at_best(int8_t num) { \n    std::cout << "I better use -Werror and -pedantic... " << +num << "\\n";\n}\n\ntemplate< typename T >\nvoid only_for_signed(T t) = delete;\n\ntemplate<>\nvoid only_for_signed<int8_t>(int8_t t) {\n    std::cout << "UB safe! 1 byte, " << +t << "\\n";\n}\n\ntemplate<>\nvoid only_for_signed<int16_t>(int16_t t) {\n    std::cout << "UB safe! 2 bytes, " << +t << "\\n";\n}\n\nint main()\n{\n    const int8_t a = 42;\n    const uint8_t b = 255U;\n    const int16_t c = 255;\n    const float d = 200.F;\n\n    warning_at_best(a); // 42\n    warning_at_best(b); // implementation-defined behaviour, no diagnostic required\n    warning_at_best(c); // narrowing, -Wconstant-conversion warning\n    warning_at_best(d); // undefined behaviour!\n\n    only_for_signed(a);\n    only_for_signed(c);\n\n    //only_for_signed(b);  \n    /* error: call to deleted function \'only_for_signed\' \n       note: candidate function [with T = unsigned char] \n             has been explicitly deleted\n       void only_for_signed(T t) = delete; */\n\n    //only_for_signed(d);\n    /* error: call to deleted function \'only_for_signed\' \n       note: candidate function [with T = float] \n             has been explicitly deleted\n       void only_for_signed(T t) = delete; */\n}\n
Run Code Online (Sandbox Code Playgroud)\n