Почему я не могу инициализировать static
членов данных в классе?
Стандарт C ++ допускает инициализацию внутри класса только статических целочисленных констант или типов перечисления.По этой причине a
разрешено инициализировать, а другие - нет.
Ссылка:
C ++ 03 9.4.2 Элементы статических данных
§4
Если член статических данных имеет константный интеграл или константный тип перечисления, его объявление в определении класса может указывать инициализатор константы, который должен быть выражением интегральной константы (5.19).В этом случае член может появляться в виде целочисленных константных выражений.Член по-прежнему должен быть определен в области имен, если он используется в программе, и определение области имен не должно содержать инициализатор.
Что такое целочисленные типы?
C ++ 03 3.9.1 Основные типы
§7
Типы bool, char, wchar_t, а также целочисленные типы со знаком и без знака называются совместноцелочисленные типы.43) Синонимом целочисленного типа является целочисленный тип.
Сноска:
43) Следовательно, перечисления (7.2) не являются целыми;однако перечисления могут быть переведены в int, unsigned int, long или unsigned long, как указано в 4.5.
Обходной путь:
Вы можете использовать enum trick чтобы инициализировать массив внутри определения вашего класса.
class A
{
static const int a = 3;
enum { arrsize = 2 };
static const int c[arrsize] = { 1, 2 };
};
Почему Стандарт не допускает этого?
Бьярн объясняет это удачно здесь :
Класс обычно объявляется в файле заголовка, а файл заголовка обычно включается во многие единицы перевода.Однако, чтобы избежать сложных правил компоновщика, C ++ требует, чтобы у каждого объекта было уникальное определение.Это правило было бы нарушено, если бы C ++ допускал в классе определение сущностей, которые должны были храниться в памяти как объекты.
Почему только static const
допускается целочисленная инициализация внутри класса?
Ответ скрыт в цитате Бьярне. Прочтите его внимательно,
"C ++ требует, чтобы у каждого объекта было уникальное определение. Это правило было бы нарушено, если бы C ++ допускал определение сущностей в классе, которое необходимо дляхраниться в памяти как объекты. "
Обратите внимание, что только static const
целые числа могут рассматриваться как константы времени компиляции.Компилятор знает, что целочисленное значение не изменится в любое время и, следовательно, он может применить свою собственную магию и применить оптимизации, компилятор просто вставляет такие члены класса, т. Е. Они больше не хранятся в памяти, так как необходимость сохранения в памяти устраняетсяон дает таким переменным исключение из правила, упомянутого Бьярне.
Следует отметить, что даже если static const
интегральные значения могут иметь инициализацию в классе, получение адреса таких переменных не допускается.Можно взять адрес статического члена, если (и только если) он имеет внеклассовое определение. Это дополнительно подтверждает рассуждения выше.
перечисления допускаются, потому что значения перечислимого типа могут использоваться там, где ожидаются целые числа. см. Цитату выше
Как это изменяется в C ++11?
C ++ 11 ослабляет ограничение в определенной степени.
C ++ 11 9.4.2 Элементы статических данных
§3
Если статический член данных имеет константный литеральный тип, его объявление в определении класса может указывать brace-or-equal-initializer , в котором каждый initializer-clause , который является выражение-присваивание является константным выражением. Статический член данных литерального типа может быть объявлен в определении класса с помощью constexpr specifier;
, если это так, его объявление должно указывать brace-or-equal-initializer , в котором каждое initializer-clause это выражение присваивания является константным выражением. [Примечание: в обоих этих случаях член может появляться в константных выражениях. - примечание] Элемент должен быть определен в области имен, если он используется в программе, а определение области имен не должно содержать инициализатор.
Кроме того, C ++ 11 будет позволять (§12.6.2.8) не статический элемент данных инициализироваться там, где он объявлен (в своем классе). Это будет означать гораздо более легкую семантику пользователя.
Обратите внимание, что эти функции еще не были реализованы в последней версии gcc 4.7, поэтому вы все равно можете получить ошибки компиляции.