C++ разница между определением и инициализацией?

Липпман, Лажойе... Программирование C++. Базовый курс. 5-е издание. 390 страница. Из прочитанного понял следующее: Статические переменные класса, обязательно надо определять вне класса. Но если очень хочется инициализировать внутриклассовым инициализатором, то эта переменная должна быть целочисленной и константной. введите сюда описание изображения

Обратите внимание на: "не следует определять отдельно" и "определение для этого члена необходимо". Но только static constexpr (или к примеру const) int не возможно определить как

constexpr int Account::period = 30;

То есть почему в одном абзаце написано то, что не работает дляя static const int? Или Я не правильно понял и имелось ввиду что обсуждается "в общем" любой static int? Хотя в самом незу часть кода показывает что всё таки можно:

 constexpr int Account::period;

То есть получается что такой код тоже называется определением, но у него нет же инициализатора. Я у себя в голове отложил что определение может быть только одно и оно должно соправаждаться инициализацией, или же инициализация в классе и определение в не класса это разные вещи? Я вот всегда думал что инициализация сразу при объявлении равносильно определению. Или Я что то не правильно понял? Подскажите пожалуйста. Спасибо.


Ответы (1 шт):

Автор решения: HolyBlackCat

Во-первых, это устаревшая информация времен C++14. В С++17 и новее static constexpr переменным никогда не нужно дополнительное определение, все работает без него (независимо от того как переменная используется) (для любого типа, не только целочисленных).

Отдельное определение все еще нужно если написать static const int x = 42; (то есть не constexpr, не inline, есть инициализатор, и целочисленного типа (для других типов не компилируется)).

Обратите внимание на: "не следует определять отдельно" и "определение для этого члена необходимо".

Здесь "не следует" - это рекомендация (рекомендация не писать лишнего). Не будет ничего страшного если написать. И дальше автор наоборот рекомендует всегда писать.

Остальную часть вопроса не понимаю, подозреваю что тут путаница в терминах.

  • "Иницаилизация" в С++ - это создание объекта во время работы программы (обычно с установкой начального значения, но строго говоря это даже без него "инициализация"). В общепрограммистском смысле это означает "установка начального значения".

  • "Определение" - это запись в коде программы, которая говорит компилятору создать объект, определенного вида (обычно тип имя; или тип имя = значение;). Все (?) определения также считаются объявлениями.

  • "Объявление" - это либо определение, либо запись в коде программы, сообщающая компилятору, что объект скорее всего будет определен где-то еще, и позволяет его использовать не видя определения. Тоже обычно имеет вид тип имя;.

А теперь с примерами:

struct A
{
  • static int v1; - Объявление.
  • static const int v2; - Объявление.
  • static constexpr int v3; - Не компилируется.
  • static int v4 = 42; - Не компилируется.
  • static const int v5 = 42; - Объявление, а точнее "инициализируещее объявление" (хотя в C++14 такого термина еще не было).
  • static constexpr int v6 = 42; - Объявление в C++14, определение в C++17. В С++17 автоматически inline.

Ну и inline-переменные из C++17.

  • inline static int v7 = 42; - Определение.
  • inline static const int v8 = 42; - Определение.
  • inline static constexpr int v9 = 42; - Определение.
};
  • int A::v1; - Определение.
  • const int A::v2 = 42; - Определение, без инициализатора не компилируется.
  • const int A::v5; - Определение.
  • const int A::v6; - Определение. В С++17 это уже второе объявление той же переменной, но не ошибка, потому что inline.

То есть наличие инициализатора - не означает что это определение. Это может быть "инициализирующее объявление". И наоборот, отсутствие инициализатора - не значит что это не определение.

Ссылки:

Для общего развития, использование переменной не в "контекстах, где компилятор может подставить [ее] значение" называется odr-use и требует существования определения.

→ Ссылка