= 函数声明后删除的含义

发布于 2024-10-29 06:05:50 字数 217 浏览 5 评论 0原文

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

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

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

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

What does = delete mean in that context?

Are there any other "modifiers" (other than = 0 and = delete)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

宛菡 2024-11-05 06:05:50

删除函数是C++11 功能

现在可以表达“禁止复制”这一常见习语了
直接:

<前><代码>类 X {
// ...
X&运算符=(const X&)=删除; // 禁止复制
X(const X&) = 删除;
};

[...]

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

<前><代码>结构Z {
// ...

Z(长长); // 可以用 long long 初始化
Z(长)=删除; // 但不少于
};

Deleting a function is a C++11 feature:

The common idiom of "prohibiting copying" can now be expressed
directly:

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

[...]

The "delete" mechanism can be used for any function. For example, we
can eliminate an undesired conversion like this:

struct Z {
    // ...

    Z(long long);     // can initialize with a long long      
    Z(long) = delete; // but not anything less
};
冰火雁神 2024-11-05 06:05:50
  1. = 0 表示函数是纯虚函数,您无法从此类实例化对象。你需要从它派生并实现这个方法
  2. =delete意味着编译器不会为你生成那些构造函数。 AFAIK 仅允许在复制构造函数和赋值运算符上使用。但我不太擅长即将到来的标准。
  1. = 0 means that a function is pure virtual and you cannot instantiate an object from this class. You need to derive from it and implement this method
  2. = delete means that the compiler will not generate those constructors for you. AFAIK this is only allowed on copy constructor and assignment operator. But I am not too good at the upcoming standard.
帝王念 2024-11-05 06:05:50

摘自《The C++ 编程语言 [第 4 版] - Bjarne Stroustrup》一书的这段摘录讨论了使用 =delete 背后的真正目的

3.3.4 抑制操作

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

类形状 {
民众:
  形状(常量形状&)=删除; // 没有复制操作
  形状&运算符=(const Shape&)=删除;

  形状(形状&&)=删除; // 没有移动操作
  形状&运算符=(形状&&)=删除;
  形状();
    // ...
};

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

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

This excerpt from The C++ Programming Language [4th Edition] - Bjarne Stroustrup book talks about the real purpose behind using =delete:

3.3.4 Suppressing Operations

Using the default copy or move for a class in a hierarchy is typically
a disaster: given only a pointer to a base, we simply don’t know what
members the derived class has, so we can’t know how to copy
them
. So, the best thing to do is usually to delete the default copy
and move operations, that is, to eliminate the default definitions of
those two operations:

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();
    // ...
};

Now an attempt to copy a Shape will be caught by the compiler.

The =delete mechanism is general, that is, it can be used to suppress any operation

木緿 2024-11-05 06:05:50

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

由于似乎没有其他人回答这个问题,我应该提到还有 = default

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

Are there any other "modifiers" (other than = 0 and = delete)?

Since it appears no one else answered this question, I should mention that there is also = default.

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

一枫情书 2024-11-05 06:05:50

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

//  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 个)不允许删除。

您还可以使用“=删除”来禁止不同大小值的隐式提升...示例

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

The coding standards I've worked with have had the following for most of class declarations.

//  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)

If you use any of these 6, you simply comment out the corresponding line.

Example: class FizzBus require only dtor, and thus do not use the other 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)

We comment out only 1 here, and install the implementation of it else where (probably where the coding standard suggests). The other 5 (of 6) are disallowed with delete.

You can also use '= delete' to disallow implicit promotions of different sized values ... example

// 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;
浊酒尽余欢 2024-11-05 06:05:50

删除的函数是隐式内联的

(现有答案的附录)

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

引用 [dcl.fct.def.delete]/4< /a>:

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

struct sometype {
  某种类型();
};
某种类型::某种类型() = 删除; // 格式错误;不是第一次声明

结束示例

删除了定义的主要功能模板可以进行专门化

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

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

A deleted function is implicitly inline

(Addendum to existing answers)

... And a deleted function shall be the first declaration of the function (except for deleting explicit specializations of function templates - deletion should be at the first declaration of the specialization), meaning you cannot declare a function and later delete it, say, at its definition local to a translation unit.

Citing [dcl.fct.def.delete]/4:

A deleted function is implicitly inline. ( Note: The one-definition
rule
([basic.def.odr])
applies to deleted definitions. — end note ] A deleted definition
of a function shall be the first declaration of the function or, for
an explicit specialization of a function template, the first
declaration of that specialization. [ Example:

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

end example )

A primary function template with a deleted definition can be specialized

Albeit a general rule of thumb is to avoid specializing function templates as specializations do not participate in the first step of overload resolution, there are arguable some contexts where it can be useful. E.g. when using a non-overloaded primary function template with no definition to match all types which one would not like implicitly converted to an otherwise matching-by-conversion overload; i.e., to implicitly remove a number of implicit-conversion matches by only implementing exact type matches in the explicit specialization of the non-defined, non-overloaded primary function template.

Before the deleted function concept of C++11, one could do this by simply omitting the definition of the primary function template, but this gave obscure undefined reference errors that arguably gave no semantic intent whatsoever from the author of primary function template (intentionally omitted?). If we instead explicitly delete the primary function template, the error messages in case no suitable explicit specialization is found becomes much nicer, and also shows that the omission/deletion of the primary function template's definition was intentional.

#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< ...
}

However, instead of simply omitting a definition for the primary function template above, yielding an obscure undefined reference error when no explicit specialization matches, the primary template definition can be deleted:

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

Yielding a more more readable error message, where the deletion intent is also clearly visible (where an undefined reference error could lead to the developer thinking this an unthoughtful mistake).

Returning to why would we ever want to use this technique? Again, explicit specializations could be useful to implicitly remove implicit conversions.

#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; */
}
不离久伴 2024-11-05 06:05:50

=delete 是 C++11 中引入的功能。根据 =delete 不允许调用该函数。

详细。

假设在一个班级里。

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

  }
};

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

= delete is a feature introduce in C++11. As per =delete it will not allowed to call that function.

In detail.

Suppose in a class.

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

  }
};

While calling this function for obj assignment it will not allowed. Means assignment operator is going to restrict to copy from one object to another.

一梦等七年七年为一梦 2024-11-05 06:05:50

一个总结一些常见用法的小例子:

class MyClass
{
public:
    // Delete copy constructor:
    // delete the copy constructor so you cannot copy-construct an object
    // of this class from a different object of this class
    MyClass(const MyClass&) = delete;

    // Delete assignment operator:
    // delete the `=` operator (`operator=()` class method) to disable copying
    // an object of this class
    MyClass& operator=(const MyClass&) = delete;

    // Delete constructor with certain types you'd like to
    // disallow:
    // (Arbitrary example) don't allow constructing from an `int` type. Expect
    // `uint64_t` instead.
    MyClass(uint64_t);
    MyClass(int) = delete;

    // "Pure virtual" function:
    // `= 0` makes this is a "pure virtual" method which *must* be overridden 
    // by a child class
    virtual uint32_t getVal() = 0;
};

TODO:

  1. 我仍然需要制作一个更彻底的示例,并运行它以显示一些用法和输出以及相应的错误消息。

另请参阅

  1. https://www.stroustrup.com/C++11FAQ.html# default - “默认值控制:defaultdelete”部分

A small example to summarize some common usages:

class MyClass
{
public:
    // Delete copy constructor:
    // delete the copy constructor so you cannot copy-construct an object
    // of this class from a different object of this class
    MyClass(const MyClass&) = delete;

    // Delete assignment operator:
    // delete the `=` operator (`operator=()` class method) to disable copying
    // an object of this class
    MyClass& operator=(const MyClass&) = delete;

    // Delete constructor with certain types you'd like to
    // disallow:
    // (Arbitrary example) don't allow constructing from an `int` type. Expect
    // `uint64_t` instead.
    MyClass(uint64_t);
    MyClass(int) = delete;

    // "Pure virtual" function:
    // `= 0` makes this is a "pure virtual" method which *must* be overridden 
    // by a child class
    virtual uint32_t getVal() = 0;
};

TODO:

  1. I still need to make a more thorough example, and run this to show some usages and output, and their corresponding error messages.

See also

  1. https://www.stroustrup.com/C++11FAQ.html#default - section "control of defaults: default and delete"
手心的温暖 2024-11-05 06:05:50

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

This is new thing in C++ 0x standards where you can delete an inherited function.

℉絮湮 2024-11-05 06:05:50
  • 是的,函数还有其他限定符,其中之一是右值引用限定符::

事实上

struct S
{
    void contextDetector() && {}
};

int main()
{
    S{}.contextDetector();  // ok

    S s;
    s.contextDetector();  // error: passing 'S' as 'this' argument discards qualifiers
}

,您可以通过使用左值引用限定符来反转此行为:

struct S
{
    void contextDetector() & {}
};

int main()
{
    S{}.contextDetector();  // error
}
  • 当然,您已经了解 const 限定符,它同样将“this”限制在 const 上下文中。

  • 易失性将执行相同的上下文限制。
    您知道有 noexcept除了(类型..)也是如此

  • constexpr ,必须写在左侧。

  • consteval 自 C++20 起用于严格的构建时间限制上下文。

  • static(左侧),这将限制链接,或使其成为类范围而不是方法。

  • extern 我认为如果没有立即定义它通常是隐含的。

  • inline(左侧)同样会改变链接规则。

  • 您还可以添加属性,虽然它们不是限定符,但从语法上讲,我们应该能够将它们写在左侧和右侧,但我没有找到让我们写在右侧的编译器:

:

[[nodiscard]] int f();

尾随返回类型:

auto f() -> void

请注意,如您所知,资格应出现在 ->

struct X
{
    int mem;

    X() try : mem(mayThrowFuncCall()) {}
    catch (...)
    {
    }
};
  • 成员初始化列表,在构造函数的情况下,您知道 S() : mem(0) {}
  • a requires模板概念子句
  • 请注意,在 C++26 中,您将能够在删除子句中设置错误消息

::

int f() = delete("cant use me");
  • Yes there are other qualifiers for functions, one of which is the rvalue reference qualifier:

:

struct S
{
    void contextDetector() && {}
};

int main()
{
    S{}.contextDetector();  // ok

    S s;
    s.contextDetector();  // error: passing 'S' as 'this' argument discards qualifiers
}

As a matter of fact, you can reverse this behavior by using a lvalue reference qualifier:

struct S
{
    void contextDetector() & {}
};

int main()
{
    S{}.contextDetector();  // error
}
  • Of course you already know about the const qualifier, which similary restricts "this" to be in a const-context.

  • volatile will do the same context restriction.
    There is noexcept which you know. except(types..) too

  • constexpr which has to be written on the left side.

  • consteval since C++20 for strict build time restriction context.

  • static (left side) which will limit linkage, or make it a class scope instead of method.

  • extern I think it's usually implied if not immediately defined.

  • inline (left side) which similarly changes linkage rules.

  • You can also add attributes, they aren't qualifiers though but syntaxically we are supposed to be able to write them on the left and right, but I didn't find a compiler that let us write on the right:

:

[[nodiscard]] int f();

trailing return type:

auto f() -> void

Note that qualification shall appear before ->

  • virtual as you know.
  • override in case of virtual functions that you want to make sure respects its base prototype.
  • final if that's the lowest virtual function in the type hierarchy.
  • explicit for converting constructors.
  • friend on the left side.
  • try a very odd one. function try blocks: https://en.cppreference.com/w/cpp/language/try#Function_try_block

:

struct X
{
    int mem;

    X() try : mem(mayThrowFuncCall()) {}
    catch (...)
    {
    }
};
  • A member initialization list, in case of constructors, you know the stuff S() : mem(0) {}
  • a requires clause for concepts on templates
  • Note that in C++26 you'll be able to set an error message in the delete clause:

:

int f() = delete("cant use me");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文