Почему переменные non-const, non-int / enum должны быть инициализированы вне определения?

Я понимаю, что в объявлении classа могут быть инициализированы только элементы данных, которые являются static, const и int / enum (pre c ++ 11). «Все остальные члены статических данных должны быть определены в глобальной области пространства имен (т. Е. Вне тела определения classа) и могут быть инициализированы только в этих определениях».

Почему в определении classа не могут быть инициализированы другие статические члены данных? Была ли конкретная причина, почему это было запрещено?

Если члены данных относятся к classу, то почему они объявлены в области глобального пространства имен, а не в какой-либо области, относящейся к их classу?

Почему в определении classа не могут быть инициализированы другие статические члены данных? Была ли конкретная причина, почему это было запрещено?

Скорее всего, потому, что C ++ имеет отдельные единицы перевода. Компилятору необходимо выбрать объектный файл, в котором будет помещена логика инициализации для этих символов. Принуждение этого к конкретному исходному файлу упрощает это решение для компилятора.

Если члены данных относятся к classу, то почему они объявлены в области глобального пространства имен, а не в какой-либо области, относящейся к их classу?

Потому что именно так C ++ делает членов classа. Это не отличается от других членов classа, таких как функции-члены:

Заголовок файла:

namespace example { // Class declared in header struct some_class { // Member variable static float example; // Member function void DoStuff() const; }; } 

Исходный файл:

 namespace example { // Implement member variable float some_class::example = 3.14159; // Implement member function void some_class::DoStuff() const { //.... } } 

Существует специальное исключение, позволяющее инициализировать статические константные интегральные элементы в заголовке, поскольку оно позволяет компилятору рассматривать их как константы времени компиляции. То есть вы можете использовать их для определения размеров массивов или других подобных бит в определении classа.

Почему в определении classа не могут быть инициализированы другие статические члены данных? Была ли конкретная причина, почему это было запрещено?

В общем, все статические объекты требуют определения в одной единице перевода, так что у них есть четко определенный адрес. В качестве особого исключения статические, постоянные, нелетучие члены classа не нуждаются в определении, если их адрес не требуется, и они имеют достаточно простой тип, чтобы их значение можно было заменить постоянной времени компиляции.

Исторически «достаточно простая» была определена как интегральный или перечисляемый тип; C ++ 11 расширяет его, чтобы включить любой литеральный тип с помощью constexpr .

Если члены данных относятся к classу, то почему они объявлены в области глобального пространства имен, а не в какой-либо области, относящейся к их classу?

Они не объявляются в глобальной области пространства имен. Они объявляются и охватываются внутри classа.

Если вы имеете в виду, почему они определены вне определения classа, это потому, что должно быть только одно определение статического члена во всей программе; но class должен быть определен в каждой единицы перевода, которая его использует.

Почему в определении classа не могут быть инициализированы другие статические члены данных? Была ли конкретная причина, почему это было запрещено?

Элемент статических данных во многом (и особенно с точки зрения компилятора) похож на объект данных области пространства видимости с внешней связью.

Объявление статического члена данных – это просто объявление, а не определение. Он похож на extern объявление глобального объекта и должен быть включен в любую единицу перевода, где объект может использоваться.

Определение должно отображаться ровно в одной единицы перевода, и это выражение принадлежит инициатору. Если выражение не соответствует строгим критериям постоянного выражения, его значение может зависеть от времени и контекста, которые он вызывает. Наличие такого выражения инициализатора в нескольких единицах перевода сделает контекст выполнения и время инициализации и, наконец, начальное значение неоднозначным.

Константа времени компиляции с classом считалась достаточно ценной, чтобы сделать исключение для определенных типов постоянных статических членов (которые затем могут использоваться для инициализации перечислений или указания размеров массива и т. Д.). С постоянными выражениями, по меньшей мере, сложнее случайно брать различные значения инициализатора в разных единицах перевода. Эта концепция была расширена на C ++ 11 с членами constexpr .

Если члены данных относятся к classу, то почему они объявлены в области глобального пространства имен, а не в какой-либо области, относящейся к их classу?

Объявление находится в пределах classа. Объявление без определения буквально входит в определение classа, и определение появляется в области пространства имен, как и любое другое внеclassное определение члена classа. Имя члена квалифицируется именем classа, поэтому оно явно обозначается как член classа, и выражение инициализатора фактически считается находящимся в пределах classа (по крайней мере, в C ++ 11, у меня нет C + +98/03, ansible здесь).

Вы должны смотреть на это наоборот. В основном, статические члены данных должны быть определены и инициализированы вне определения classа в исходном файле. Существует исключение для static const int поскольку оно позволяет избежать различных уродливых обходных решений для определения размера массива-члена.

Они будут повторно инициализироваться каждый раз, когда class будет создан. Каждый раз, когда вы создаете новый объект типа Foo, статические переменные для всех Foos будут сброшены до их начального значения, что, вероятно, не то, что вы хотите. Поэтому, если вы хотите использовать статические переменные с вашим объектом, они либо a) не могут изменить свое значение, что означает, что повторная инициализация их на одно и то же значение является безопасным, или b) может быть изменена только вне контекста функции инициализатора.