Почему мой компилятор не распознает "Bond() = default;"?

Пожалуйста, посмотрите на этот код

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
}

При попытке компиляции я получаю сообщение об ошибке:

ошибка C2280: 'Bond :: Bond (void)': попытка обратиться к удаленной функции ".

Это не нарушение "правила 3", так как я добавил конструктор по умолчанию.

Почему компилятор не распознает Bond() = default; ?

Ответ 1

На вас влияет раздел [class.default.ctor] p2 проекта стандарта C++ (или [class.ctor] p5 в C++ 11), в котором говорится:

По умолчанию конструктор по умолчанию для класса X определяется как удаленный, если:
...
- любой не вариантный нестатический член данных типа с константным типом (или его массив) без инициализатора скобок или равенства не имеет предоставленного пользователем конструктора по умолчанию,
...

Возможно, ключом к решению вашей проблемы является фраза без инициализации-скобки или равных-инициализаторов, поэтому, если вы предоставите инициализатор скобок-или-равных, которая решит вашу проблему, например:

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

Brace-or-equal-initializer не требует фигурных скобок, мы можем увидеть следующую грамматику:

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

но использование равномерной инициализации имеет некоторые преимущества, такие как сужение суженных преобразований, поэтому их стоит использовать.

И gcc, и clang предоставляют более значимую диагностику для этого, см. Сеанс Godbolt. Иногда может быть полезно попробовать свой код на нескольких компиляторах, особенно если у вас есть минимальный тестовый пример, например, такой как clang:

 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;
              ^
...

Ответ 2

Конструктор по умолчанию подавляется, поскольку существуют постоянные члены, которые необходимо явно инициализировать.

Поэтому из-за этого подавления запись Bond() = default не вводит конструктор по умолчанию.

(Вы можете увидеть этот эффект, удалив все конструкторы в классе - вы все еще не можете создать экземпляр b.)

Если вы отбросите const от участников, то все будет хорошо; хотя другой альтернативой является предоставление инициализатора скобок или одинакового для каждого const члена;

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

например.

Ответ 3

Другое исправление - указать значение по умолчанию в объявлении констант:

const int payments_per_year = {12};

Это все еще может быть переопределено ценным конструктором, но позволяет конструктору по умолчанию продолжаться.

Это также очень гибкий способ упростить несколько вариантов конструктора.