Ініціалізація члена 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.