353
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)?

0

10 Answers 10

282

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 }; 
Sign up to request clarification or add additional context in comments.

5 Comments

Isn't the traditional method to "prohibit copying" just to make the copy-ctor and operator= "private?" This goes a bit further and instructs the compiler to not even generate the functions. If they're both private and =delete, is copying doubly-prohibited?
@Reb, =delete makes the method inaccessible even from contexts that can see private methods (i.e. within the class and its friends). This removes any uncertainty when you're reading the code. @Prasoon, that second example is still only deleting constructors - it would be nice to see a deleted operator long () for example.
@Reb.Cabin Using = delete is better than using private or other similar mechanisms because usually you want the forbidden function to be visibly declared and considered for overload resolution etc., so that it can fail as early as possible and provide the clearest error to the user. Any solution which involves "hiding" the declaration reduces this effect.
Is there a special reason to make the copy constructor public and apply the delete keyword. Why not leave the constructor private and apply the keyword?
Not always. You can't delete a base class virtual function in derived.
120
  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.

5 Comments

There are some other uses of the =delete syntax. For example you can use it to explicitly disallow some kind of implicit conversions that might take place with the call. For this you just delete the overloaded functions. Have a look a the Wikipedia page on C++0x for more info.
I will do that as soon as I find some. Guess it it time to catch up with c++0X
Yeah, C++0x rocks. I can't wait for GCC 4.5+ to be more common, so I can start using lambdas.
The description for = delete is not entirely correct. = delete can be used for any function, in which case it is explicitly marked as deleted and any use results in a compiler error. For special member functions, this also means in particular that they then aren't generated for you by the compiler, but that is only a result of being deleted, and not what = delete truly is.
@MicroVirus I suppose your final point is really a result of the special functions having been declared by the programmer - declaring them as deleted is just an example of this.
57

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

Comments

24

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

Comments

9

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; 

2 Comments

Creating an object of a class with a deleted constructor is illegal.
@Nikos - No -- You simply need to provide a constructor. The example of Adding " T() = delete; " stops the compiler from adding a (do minimal) default ctor, which is occasionally useful, but still allows you to add a (presumably-does-something-useful) ctor.
6

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

Comments

3

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"

2 Comments

are you sure this compiles? You don't have "virtual" keyword next to getVal() method, and struct definition does not end with ";"
@PiotrNycz, great catches! You are correct. I have updated the answer. No, I haven't compiled nor run this code directly.
2

= 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.

Comments

0

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

2 Comments

You can delete any function. E.g void foo(int); template <class T> void foo(T) = delete; stops all implicit conversions. Only arguments of int type are accepted, all others will try to instantiate a "deleted" function.
= delete also works with non-member functions. This is useful for preventing implicit type conversions.
0
  • 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"); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.