40

Please look at this code

class Bond
{
    public:
        Bond(int payments_per_year, int period_lengths_in_months);
        Bond() = default;

    private:
        const int payments_per_year;
        const int period_length_in_months;
    };

int main()
{
    Bond b; // Error here
}

When attempting to compile I get an error:

error C2280: 'Bond::Bond(void)': attempting to reference a deleted function".

It's not a "rule of 3" violation since I've added the default constructor back.

Why doesn't the compiler recognise Bond() = default;?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 2
    I have other error `uninitialized const member in 'const int'`. When you initialize constant member no more error produced. – serge Nov 09 '18 at 12:14
  • 2
    Rule of three violation has got nothing to do with the problem at all, regardless of the presence of a (default) constructor. – Konrad Rudolph Nov 09 '18 at 15:04
  • 2
    `= default`ing a special member doesn't mean that it exists, but that the implicit one is generated. If the implicitly generated one doesn't exist, then you get this. – Rakete1111 Nov 09 '18 at 15:06
  • 4
    Even though we are quite likely to recognize the compiler from the error cited in this case, any question with "why my compiler" would benefit greatly from indicating *which* compiler, and *which* version of it. – Matthieu M. Nov 09 '18 at 20:19

3 Answers3

51

The default constructor is suppressed since there are constant members that need to be explicitly initialised.

Therefore, due to that suppression, writing Bond() = default does not reintroduce the default constructor.

(You can see this effect by removing all the constructors in the class - you still can't instantiate a b.)

If you drop the const from the members then all will be well; although another alternative is to supply a brace-or-equal-initializer for each const member;

const int payments_per_year = 2;
const int period_length_in_months = 6;

for example.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
27

You are being affected by section [class.default.ctor]p2 of the draft C++ standard (or [class.ctor]p5 in C++11) which says:

A defaulted default constructor for class X is defined as deleted if:
...
- any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer does not have a user-provided default constructor,
...

They possible key to fixing your issue is with the phrase with no brace-or-equal-initializer so if you provide brace-or-equal-initializer that will fix your issue e.g.:

const int payments_per_year{12};
const int period_length_in_months{48};

brace-or-equal-initializer does not require braces, we can see this the grammar:

brace-or-equal-initializer:
    = initializer-clause
    braced-init-list

but using uniform initialization has some advantages such as making narrowing conversions ill-formed that it is worth getting used to using them.

Both gcc and clang provide more meaningful diagnostics for this see the live godbolt session. Sometimes it can be helpful to try your code on multiple compilers, especially if you have a minimal test case like this e.g. clang says:

 warning: explicitly defaulted default constructor is implicitly deleted [-Wdefaulted-function-deleted]
    Bond() = default;
    ^
 note: default constructor of 'Bond' is implicitly deleted because field 'payments_per_year' of const-qualified type 'const int' would not be initialized
    const int payments_per_year;
              ^
...
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
24

Another fix, is to specify a default value in the declaration of the constants:

const int payments_per_year = {12};

This can still be overridden by the valued constructor, but allows the default constructor to proceed.

This is also a very flexible way to simplify your multiple constructor cases.

Gem Taylor
  • 5,381
  • 1
  • 9
  • 27
  • 1
    Do we need the braces? – Paul Sanders Nov 09 '18 at 14:35
  • 2
    Equals *and* braces is definitely weird. Not sure what it actually means. Init from an initializer_list? Anyway: in-class member initializers are – well – initializers, and the syntax rules for initialization apply. That means either = or {}. – besc Nov 09 '18 at 14:57
  • 6
    @PaulSanders no, you don't need braces but uniform initialization has a lot of advantages such as making narrowing conversions ill-formed that it is worth getting used to using them. See my update below. – Shafik Yaghmour Nov 09 '18 at 17:17
  • 4
    @ShafikYaghmour: You could keep the braces and drop the equal though. – Matthieu M. Nov 09 '18 at 20:20
  • @MatthieuM. that is exactly what I did in my answer. – Shafik Yaghmour Nov 10 '18 at 22:24
  • 1
    @ShafikYaghmour: Yep, and that's part of the reason why I much prefer your answer (the other being actual standard quotes). – Matthieu M. Nov 11 '18 at 12:02
  • You are right.. the braces and the equals are not both needed. It is a bad habit I got into when adding this "hey guys a new feature" to my code. It sure makes these default constructions stand out nicel.y – Gem Taylor Nov 12 '18 at 18:14