Ініціалізація члена const в оголошенні класу в C ++


80

У PHP та C # константи можуть бути ініціалізовані, коли вони оголошені:

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

У мене є така декларація С ++ функтора, яка використовується з іншим класом для порівняння двох математичних векторів:

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]

Причиною цього є те, що стандарт С ++ не визначає, яким чином слід реалізовувати плаваючу крапку, і залишається за процесором. Щоб обійти це та інші обмеження constexprбуло введено.


14

Так. Просто додайте constexprключове слово, як сказано в помилці.


1
Можливо, він не хоче вимагати C ++ 11 для свого проекту?
Marc Mutz - mmutz

6
Проблема, про яку він згадує, була введена через те, що він намагався скомпілювати як C ++ 11, тобто він хотів би підтримати C ++ 11 :)
Невідомо1987

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, але я підозрюю, що ви могли б зробити подібне з іншими компіляторами.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.