Инициализация константного члена в объявлении класса в C ++

80

В PHP и C # константы могут быть инициализированы по мере их объявления:

class Calendar3
{
    const int value1 = 12;
    const double value2 = 0.001;
}

У меня есть следующее объявление C ++ функтора, которое используется с другим классом для сравнения двух математических векторов:

struct equal_vec
{
    bool operator() (const Vector3D& a, const Vector3D& b) const
    {
        Vector3D dist = b - a;
        return ( dist.length2() <= tolerance );
    }

    static const float tolerance = 0.001;
};

Этот код без проблем компилировался с g ++. Теперь в режиме C ++ 0x (-std = c ++ 0x) компилятор g ++ выдает сообщение об ошибке:

ошибка: 'constexpr' необходим для внутриклассной инициализации статического элемента данных 'толерантность' нецелого типа

Я знаю, что могу определить и инициализировать этот static constчлен вне определения класса. Кроме того, нестатический постоянный член данных может быть инициализирован в списке инициализаторов конструктора.

Но есть ли способ инициализировать константу в объявлении класса, как это возможно в PHP или C #?

Обновить

Я использовал staticключевое слово только потому, что можно было инициализировать такие константы в объявлении класса в g ++. Мне просто нужен способ инициализировать константу в объявлении класса, независимо от того, объявлено оно как staticили нет.

эспрессо
источник
5
I used static keyword just because it was possible to initialize such constants within the class declaration in g++. I just need a way to initialize a constant in a class declaration no matter if it declared as static or not.Это неправильный способ решать, быть участником staticили нет. Никогда не позволяйте лексической лени определять семантику вашего кода.
Гонки легкости на орбите
That's the wrong way to decide whether a member should be static or not.Я не согласен. Думаю, для постоянных участников это не имеет значения.
ezpresso
3
@expresso: Вовсе нет. Вы можете инициализировать staticнепостоянный член информацией, зависящей от экземпляра. То, что вы решили, что ваша константа является свойством типа, а не конкретного экземпляра, является причиной сделать это static, а не потому, что вам понравился ярлык для набора текста.
Гонки за легкостью на орбите,
@lightless: Что ж, это возможно, но я не вижу причин для использования инициализации одних и тех же констант, зависящих от экземпляра, с разными значениями. Я использовал для этого неконстантные поля класса!
ezpresso
4
Почему, если они никогда не меняются после создания объекта? struct myType { const std::time_t instantiated; myType() : instantiated(std::time(0)) {} };Все, что может быть, const должно быть const; это относится как к членам, так staticи к нечленам static.
Гонки за легкостью на орбите,

Ответы:

136

В C ++ 11 staticэлементы static constexprданных , не являющиеся членами данных, элементы static constданных и элементы данных интегрального или перечисляемого типа могут быть инициализированы в объявлении класса. например

struct X {
    int i=5;
    const float f=3.12f;
    static const int j=42;
    static constexpr float g=9.5f;
};

В этом случае iчлен всех экземпляров класса Xинициализируется 5конструктором, созданным компилятором, а fчлен инициализируется значением 3.12. static constЭлемент данных jинициализируется 42, а static constexprчлен данных gинициализируется 9.5.

Поскольку floatи doubleне относятся к целочисленному или перечислимому типу, такие члены должны быть constexprили не быть , staticчтобы инициализатор в определении класса был разрешен.

До C ++ 11 static constинициализаторы в определении класса могли иметь только члены данных целочисленного или перечислимого типа.

Энтони Уильямс
источник
Есть ли ссылка на ту часть стандарта, где это было указано? Я только начал его использовать (хотя и компилировать как C ++ 14) и рад, что он работает, поскольку я собираюсь сэкономить много времени, заставив компилятор сделать это за меня. Однако, пока не прочитал ваш ответ, я не был уверен, должно ли оно работать! Официальное слово укрепит мою уверенность, ха-ха. Fwiw у меня тоже есть работа с участниками char const n[3]{'a', 'b', 'c'};.
underscore_d
Интересно, почему, static const intно static constexpr float? что это значит float and double are not of integral or enumeration type?
mrgloom
@mrgloom: да, float и double - нет. Если тип данных можно преобразовать в целое число, то это целочисленный тип.
ppadhy
45

Инициализация статических переменных-членов, отличных от типов const int, не является стандартом C ++ до C ++ 11. Компилятор gcc не предупредит вас об этом (и, тем не менее, создаст полезный код), если вы не укажете этот -pedanticпараметр. Затем вы должны получить ошибку, похожую на:

const.cpp:3:36: error: floating-point literal cannot appear in a constant-expression
const.cpp:3:36: warning: ISO C++ forbids initialization of member constant ‘tolerance’ of non-integral type ‘const float’ [-pedantic]

Причина этого в том, что стандарт C ++ не определяет, как должна быть реализована плавающая точка, и остается на усмотрение процессора. Чтобы обойти это, constexprбыли введены и другие ограничения .

Флориан
источник
14

Да. Просто добавьте constexprключевое слово, как указано в ошибке.

СтайлзКризис
источник
1
Может, он не хочет требовать C ++ 11 для своего проекта?
Марк Мутц - mmutz
6
Проблема, о которой он упоминает, возникла из-за того, что он пытался скомпилировать как C ++ 11, то есть он хотел бы поддерживать C ++ 11 :)
Unknown1987
1

Если вам это нужно только в одном методе, вы можете объявить его локально статическим:

struct equal_vec
{
    bool operator() (const Vector3D& a, const Vector3D& b) const
    {
        static const float tolerance = 0.001f;
        Vector3D dist = b - a;
        return ( dist.length2() <= tolerance );
    }
};
Питер Вуд
источник
1

Я столкнулся с настоящими проблемами, потому что мне нужен один и тот же код для компиляции с разными версиями g ++ (компилятор GNU C ++). Поэтому мне пришлось использовать макрос, чтобы увидеть, какая версия компилятора используется, а затем действовать соответственно, например

#if __GNUC__ > 5
 #define GNU_CONST_STATIC_FLOAT_DECLARATION constexpr
#else
 #define GNU_CONST_STATIC_FLOAT_DECLARATION const
#endif

GNU_CONST_STATIC_FLOAT_DECLARATION static double yugeNum=5.0;

Это будет использовать 'const' для всего до g ++ версии 6.0.0, а затем использовать 'constexpr' для g ++ версии 6.0.0 и выше. Это предположение о версии, в которой происходит изменение, потому что, честно говоря, я не замечал этого до g ++ версии 6.2.1. Чтобы сделать это правильно, вам, возможно, придется посмотреть на младшую версию и номер патча g ++, поэтому см.

https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html

для получения подробной информации о доступных макросах.

С gnu вы также можете повсюду использовать 'const', а затем скомпилировать с -fpermissive флагом, но это дает предупреждения, и мне нравится, что мои вещи компилируются чисто.

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

nilesOien
источник