128

When/why would I want to explicitly delete my constructor? Assuming the reason is to prevent its usage, why not just make it private?

class Foo
{ 
  public: 
    Foo() = delete; 
};
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
ash
  • 3,354
  • 5
  • 26
  • 33
  • 18
    It kind of goes nicely with `= default`, not even the class can use it, and I personally prefer seeing *Use of deleted function.* over *Function is private.* The former explicitly states "This is not meant to be used." If anything comes out of that, the class not being able to use it actually makes a semantical difference. – chris Dec 01 '12 at 00:03
  • 12
    Since I rarely use C++11, this is more informative to me than the OP probably even realizes. I didn't even know you *could* tag a constructor for `delete`. Both the question and Luchian's answer easily qualify as constructive. Anyone not breathing the finer points of C++ 11 but will need to soon will get something out of both. – WhozCraig Dec 01 '12 at 06:21
  • 1
    Possible duplicate of [What's the point of deleting default class constructor?](https://stackoverflow.com/questions/48664631/whats-the-point-of-deleting-default-class-constructor) – user673679 Feb 25 '18 at 16:46

4 Answers4

124

How about:

//deleted constructor
class Foo
{ 
  public: 
    Foo() = delete;     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //illegal
}

versus

//private constructor
class Foo
{ 
  private: 
    Foo() {}     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //legal
}

They're basically different things. private tells you that only members of the class can call that method or access that variable (or friends of course). In this case, it's legal for a static method of that class (or any other member) to call a private constructor of a class. This doesn't hold for deleted constructors.

Sample here.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 3
    You do not need to declare Foo() at all, if you declare Foo(int). Foo() will not be generated and thus Foo f is invalid anyway. So your example does not show the case for deleted constructor. See for yourself - http://ideone.com/mogiIF – mark Dec 01 '12 at 00:17
  • 1
    @mark I wrote 2 constructors to prove the point. I'll edit so it's clear to everyone. – Luchian Grigore Dec 01 '12 at 00:31
  • 1
    I understand the difference, I just do not understand the added value of the delete statement in general and for a constructor in particular. After all, I could specify a private default constructor without the body. Then the code also fails, only during the linking. Well, I can see that delete conveys the intention more explicitly, but that's about it. – mark Dec 01 '12 at 10:54
  • 15
    @mark Yes, that would be the C++98 way of doing things. But IMHO, conveying the intention clearly is actually a very important thing in programming in general. In this case, some readers may see a private undefined constructor and assume it's accidental and just add a definition for it, especially if the definition is as trivial as a default constructor (Yes, having a comment helps, but we'd much prefer compiler-enforcement over comment-enforcement). By having our intent clearer, we also get a much better error message which says "explicitly deleted" rather than "undefined reference". – mpark Jan 05 '14 at 21:12
  • 2
    I honestly don't understand how this answers the main question. The question in the title and OP's first question in the post was: When/why would I want to explicitly delete my constructor? – Alexander Bolinsky Oct 25 '16 at 17:20
22

Why explicitly delete the constructor?

Another reason:

I use delete when I want to assure that a class is called with an initializer. I consider it as a very elegant way to achieve this without runtime checks.

The C++ compiler does this check for you.

class Foo
{
   public:
       Foo() = delete;
       Foo(int bar) : m_bar(bar) {};
   private:
       int m_bar;
};

This - very simplified - code assures that there is no instantiation (default construction) like this: Foo foo;

gonidelis
  • 885
  • 10
  • 32
Peter VARGA
  • 4,780
  • 3
  • 39
  • 75
  • 27
    The deleted declaration is unnecessary here. It's automatically deleted with any user-provided constructor – Mike Lui Jan 15 '19 at 18:17
  • 13
    To clarify the comment by @MikeLui, the deleted declaration is unnecessary *to the compiler*. There are numerous cases when code like this should be included to declare intent *to other programmers*. – Jeff G Apr 29 '19 at 22:30
  • 2
    Along with declaring your intent, it creates an obvious place to document your reason for its deletion in the public interface, and additionally the compiler error will be something short like "use of deleted function". If `Foo` had numerous constructors, just not a default one, then `Foo foo;` would cause a much longer error listing all implicitly defined, protected and private constructors it failed to match. – sigma Dec 20 '19 at 14:12
  • 1
    I still don't get how extra line with declaration of constructor wich "= delete" keyword declares intention of "no default constructor" idea better than ... just no default constructor? Sample: I don't want to declare variable "a" in my code - what is better, to write "// int a; // no need to define variable a" or just write nothing about this variable in code? – Ezh Jul 10 '20 at 16:00
3

tl;dr

Explicit delete always works and gives more understandable error messages. Private declaration should no longer be used to prevent calls!

Detailed explanation

If you declare a function private, then it can still be called. In the case of a constructor, for example, from a static or a friend function.

With an explicit deletion of a function you express that it must never be used. This leads especially to clearly understandable error messages when trying to call such a function.

In the following class we do not have a default constructor because there is no meaningful way to implement it. The class contains a reference and it needs some object to point to.

class Foo {
private:
    int& ref_;

public:
    Foo(int& ref) : ref_(ref) {}
};

int main() {
    Foo();
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:10:9: error: no matching function for call to ‘Foo::Foo()’
   10 |     Foo();
      |         ^
main.cpp:6:5: note: candidate: ‘Foo::Foo(int&)’
    6 |     Foo(int& ref) : ref_(ref) {}
      |     ^~~
main.cpp:6:5: note:   candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: ‘constexpr Foo::Foo(const Foo&)’
    1 | class Foo {
      |       ^~~
main.cpp:1:7: note:   candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: ‘constexpr Foo::Foo(Foo&&)’
main.cpp:1:7: note:   candidate expects 1 argument, 0 provided

Deleting the constructor makes the error message short and understandable.

class Foo {
private:
    int& ref_;

public:
    Foo() = delete;
    Foo(int& ref) : ref_(ref) {}
};

int main() {
    Foo();
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:11:9: error: use of deleted function ‘Foo::Foo()’
   11 |     Foo();
      |         ^
main.cpp:6:5: note: declared here
    6 |     Foo() = delete;
      |     ^~~

If we solve this via a private declaration (without definition, which is impossible), the message looks similar at first.

class Foo {
private:
    int& ref_;

    Foo();

public:
    Foo(int& ref) : ref_(ref) {}
};

int main() {
    Foo();
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:12:9: error: ‘Foo::Foo()’ is private within this context
   12 |     Foo();
      |         ^
main.cpp:5:5: note: declared private here
    5 |     Foo();
      |     ^~~

This works great as long as you don't call the constructor from a context where the private section of the class is accessible. As written above, this can be for example a static or a friendly function. In principle, it can also be a normal function, although this use case is rather rare.

class Foo {
private:
    int& ref_;

    Foo();

public:
    Foo(int& ref) : ref_(ref) {}

    static Foo create() {
        return Foo(); // compiles fine
    }

    void foo() {
        Foo(); // compiles fine
    }

    friend void bar();
};

void bar() {
    Foo(); // compiles fine
}

int main() {}
g++ -c -o main.o main.cpp

This compiles completely without problems, the compile simply assumes that there will be a definition of Foo::Foo() somewhere else. As soon as the linker has to make an executable out of it, it will report the missing definition.

$ g++ main.o
# or as one step with compilation and linking
$ g++ main.cpp
/usr/bin/ld: /tmp/ccnhLDsv.o: in function `bar()':
main.cpp:(.text+0x23): undefined reference to `Foo::Foo()'
collect2: error: ld returned 1 exit status

Such errors are often extremely difficult to debug because they are stuck anywhere in the code base and you have no clue which file, let alone which line, the error is in.

An explicit delete, on the other hand, provides three precise error messages at the three locations where the errors are located.

class Foo {
private:
    int& ref_;

public:
    Foo() = delete;
    Foo(int& ref) : ref_(ref) {}

    static Foo create() {
        return Foo(); // error
    }

    void foo() {
        Foo(); // error
    }

    friend void bar();
};

void bar() {
    Foo(); // error
}

int main() {}
$ g++ main.cpp
main.cpp: In static member function ‘static Foo Foo::create()’:
main.cpp:10:20: error: use of deleted function ‘Foo::Foo()’
   10 |         return Foo(); // error
      |                    ^
main.cpp:6:5: note: declared here
    6 |     Foo() = delete;
      |     ^~~
main.cpp: In member function ‘void Foo::foo()’:
main.cpp:14:13: error: use of deleted function ‘Foo::Foo()’
   14 |         Foo(); // error
      |             ^
main.cpp:6:5: note: declared here
    6 |     Foo() = delete;
      |     ^~~
main.cpp: In function ‘void bar()’:
main.cpp:21:9: error: use of deleted function ‘Foo::Foo()’
   21 |     Foo(); // error
      |         ^
main.cpp:6:5: note: declared here
    6 |     Foo() = delete;
      |     ^~~

Additional information

void foo(int need_integer) {}

int main() {
    foo(5.4); // might trigger a warning, but compiles
}

Note that delete can also be used for normal functions. For example, to prevent implicit conversions.

void foo(int need_integer) {}
void foo(double) = delete;

int main() {
    foo(5);   // okay
    foo(5.4); // error
}
$ g++ main.cpp
main.cpp: In function ‘int main()’:
main.cpp:6:8: error: use of deleted function ‘void foo(double)’
    6 |     foo(5.4); // error
      |     ~~~^~~~~
main.cpp:2:6: note: declared here
    2 | void foo(double) = delete;
      |      ^~~
Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51
2

I've met with default ctors declared as 'deleted' in the source code of LLVM (in AlignOf.h for instance). The associated class templates are usually in a special namespace called 'llvm::detail'. The whole purpose there I think was that they considered that class only as a helper class. They never intended to instantiate them; only to use them within the context of other class templates with some metaprogramming tricks that run in compile time.

Eg. there's this AlignmentCalcImpl class template which is used only within another class template called AlignOf as a parameter for the sizeof(.) operator. That expression can be evaluated in compile time; and there's no need to instantiate the template -> so why not declare the default ctor delete to express this intention.

But it's only my assumption.

gybacsi
  • 51
  • 5