Почему я не могу инициализировать 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.
Обходной путь:
Вы можете использовать трюк с перечислением для инициализации массива внутри определения вашего класса.
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
Если статический член данных имеет тип константного литерала, его объявление в определении класса может указывать фигурную скобку или равный инициализатор, в котором каждое предложение инициализатора, которое является выражением присваивания, является постоянным выражением. Статический член данных литерального типа может быть объявлен в определении класса с помощью, constexpr specifier;
если это так, его объявление должно указывать на фигурную скобку или равный инициализатор, в котором каждое предложение инициализатора, которое является выражением присваиванияпостоянное выражение. [Примечание: в обоих этих случаях член может появляться в постоянных выражениях. - конец примечания] Этот член должен быть определен в области пространства имен, если он используется в программе, а определение области пространства имен не должно содержать инициализатор.
Кроме того , C ++ 11 будет разрешать (§12.6.2.8) нестатический член данных должны быть инициализированы , где она объявлена (в своем классе). Это будет означать очень простую пользовательскую семантику.
Обратите внимание, что эти функции еще не реализованы в последней версии gcc 4.7, поэтому вы все равно можете получать ошибки компиляции.