Почему статические константные числа не допускаются?
У меня есть класс, который по сути просто содержит набор константных определений, используемых в моем приложении. По какой-то причине, хотя, long
скомпилировать но float
с не
class MY_CONSTS
{
public :
static const long LONG_CONST = 1; // Compiles
static const float FLOAT_CONST = 0.001f; // C2864
};
Выдает следующую ошибку:
1>c:\projects\myproject\Constant_definitions.h(71) : error C2864: 'MY_CONSTS::FLOAT_CONST' : only static const integral data members can be initialized within a class
Я что-то пропустил?
7 ответов
Чтобы ответить на реальный вопрос, который вы задали: "потому что так говорится в стандарте".
Только переменные статических, константных, целочисленных типов (включая перечисления) могут быть инициализированы внутри объявления класса. Если компилятор поддерживает встроенную инициализацию float, это расширение. Как указывали другие, способ работы со статическими, постоянными, нецелыми переменными состоит в том, чтобы определить и инициализировать их в соответствующем исходном файле класса (не в заголовке).
C++ Стандартный раздел 9.2 "Члены класса", пункт 4:
Элемент-декларатор может содержать инициализатор константы только в том случае, если он объявляет статический член (9.4) с константным целочисленным или константным типом перечисления, см. 9.4.2.
Раздел 9.4.2 "Элементы статических данных", пункт 2:
Если член статических данных имеет константный интегральный или константный тип перечисления, его объявление в определении класса может указывать инициализатор константы, который должен быть выражением целочисленной константы (5.19). В этом случае член может появляться в виде целочисленных константных выражений. Член все еще должен быть определен в области пространства имен, если он используется в программе, и определение области пространства имен не должно содержать инициализатор.
Вы должны инициализировать их в теле одного из ваших файлов cpp:
class MY_CONSTS
{
public :
static const long LONG_CONST = 1; // Compiles
static const float FLOAT_CONST;
};
const float MY_CONSTS::FLOAT_CONST = 0.001f;
Смотрите объяснение Страуструпа. Соответствующая цитата:
Класс обычно объявляется в файле заголовка, а файл заголовка обычно включается во многие единицы перевода. Однако, чтобы избежать сложных правил компоновщика, C++ требует, чтобы у каждого объекта было уникальное определение. Это правило было бы нарушено, если бы C++ позволял в классе определять сущности, которые нужно было хранить в памяти как объекты. Смотрите D&E для объяснения компромиссов дизайна C++.
Обоснование в соответствии со стандартной формулировкой, приведенной другими, является тем же, для которого аргументы шаблона не могут быть числом с плавающей запятой. Чтобы получить согласованный результат, вам нужно, чтобы компилятор реализовал ту же оценку, что и во время компиляции, и это может быть сложным для кросс-компилятора и в случае, когда программа играет в режиме округления.
Из памяти в C++0X понятие константного выражения было расширено, и поэтому ваш код был бы действительным (но он не определен в результате того, что константные выражения с плавающей запятой одинаковы при оценке во время выполнения или во время компиляции).
В C++11 и более поздних версиях вы можете использовать constexpr для определения констант класса с плавающей запятой:
class MY_CONSTS
{
public:
static constexpr long LONG_CONST = 1;
static constexpr float FLOAT_CONST = 0.001f;
};
Как насчет:
class MY_CONSTS
{
public :
static const long LONG_CONST;
static const float FLOAT_CONST;
};
const long MY_CONSTS::LONG_CONST = 1;
const float MY_CONSTS::FLOAT_CONST = 0.001f;
(хотя я не могу дать никакого объяснения этому конкретному случаю...)
Из стандарта 9.4.2/4
Если член статических данных имеет константный интеграл или константный тип перечисления, его объявление в определении класса может указывать инициализатор константы, который должен быть выражением интегральной константы (5.19). В этом случае член может появляться в виде целочисленных константных выражений. Член по-прежнему должен быть определен в области пространства имен, если он используется в программе и определение области пространства имен не должно содержать инициализатор.
И 5.19/1:
В некоторых местах для C + + требуются выражения, которые оцениваются как интеграл или константа перечисления: как границы массива (8.3.4, 5.3.4), как выражения case (6.4.2), как длины битового поля (9.6), как инициализаторы перечислителя (7.2), как инициализаторы статических членов (9.4.2), так и как целочисленные или нетиповые аргументы шаблонного перечисления (14.3). константное выражение: условное выражение. Интегральное выражение константы может включать только литералы (2.13), перечислители, константные переменные или члены-статические данные целочисленных или перечислимых типов, инициализированных константными выражениями (8.5), нетиповые параметры шаблона интеграла или перечисления. типы и размеры выражений. Плавающие литералы (2.13.3) могут появляться, только если они приводятся к целочисленным или перечислимым типам. Можно использовать только преобразования типов в целочисленные или перечислимые типы. В частности, за исключением размера выражений, функций, объектов классов, указателей или ссылок не должны