Объявление/определение и статическое поле класса С++

Есть класс с двумя статическими полями

struct S
{
    static int a; 
    static const int b = 1;      
};

Чтобы иметь возможность использовать поле S::a напрямую или создать указатель на него, необходимо определить это поле вне класса в глобальной области видимости.

cout << S::a << endl;        // ошибка
int* ptr_a = &S::a;          // ошибка

int S::a = 1;                // обязательное условие, теперь:
cout << S::a << endl;        // ок
int* ptr_a = &S::a;          // ок

Получить же значение статического константного поля можно, а вот инициализировать указатель - по-прежнему нет.

cout << S::b << endl;        // ок
const int* ptr_b = &S::b;    // ошибка (undefined reference to S::b)

Способ исправить это простой: дописать определение вне класса.

const int S::b;              // этого уже будет достаточно, теперь
сonst int* ptr_b = &S::b;    // ок

Поясните, пожалуйста, эту тонкость. Если можно использовать значение статического константного поля S::b напрямую без дополнительного определения вне класса, то где это значение хранится? Или оно напрямую подставляется при компиляции, не помещаясь в память, и поэтому линкер выдает ошибку 'undefined reference'?

***** UPDATE 1 *****

Пока печатал вопрос, часть ответа сама нашлась. Точнее, нашлась идея посмотреть ассемблерный код. Действительно, значение подставляется напрямую - пример. Надо выяснить остальное

***** UPDATE 2 *****

Начинать, как оказалось, необходимо с уточнения, что такое "объявление/declaration" и что такое "определение/definition". Сам вопрос содержал много ошибок в терминологии (исправил их) и, разобравшись с терминами, получилось разобраться и с самим вопросом.

Ответ на английском stackoverflow
Также смотрел в черновике N4868 (главы 6 и 9)

Если вкратце отвечать на собственный же вопрос - оба поля, приведенный мной являются только лишь объявлениями, но не определениями.

struct S
{
    static int a;             // объявление
    static const int b = 1;   // тоже объявление!
};

Однако, поскольку поле b является константным, его значение можно использовать напрямую без определения и без выделения памяти (см. update 1). Ну а поскольку определения нет, то получить указатель невозможно = получаем ошибку линкера.

Отдельная благодарность господину Alex Guteniev за указание на то, что static constexpr является определением.

По главе 6.2 пункту (2.20) стандарта и собранным ответам набросал пример.


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

Автор решения: Alex Guteniev

Определение вне класса требуется для ODR-use (не знаю русскоязычного термина).

Или оно напрямую подставляется при компиляции, не помещаясь в память

Да. Использование константы времени компиляции не является ODR-use.

Начиная с C++17:

  • Для static constexpr членов не обязательно определение вне класса, даже если они ODR-used.
  • Для static inline членов не нужно определение вне класса.
→ Ссылка