C ++ 17 вводить вбудовані змінні
C ++ 17 виправляє цю проблему для constexpr static
змінних членів, які потребують позалінійного визначення, якщо вона була odr-використана. Дивіться другу половину цієї відповіді, щоб отримати докладніші відомості про попередню C ++ 17.
Пропозиція P0386 Inline Змінні вводить можливість застосувати inline
специфікатор до змінних. Зокрема, до цього випадку constexpr
мається inline
на увазі статична змінна елемента. Пропозиція говорить:
Вбудований специфікатор може застосовуватися як до змінних, так і до функцій. Змінна, оголошена вбудованим рядком, має таку саму семантику, що і функція, оголошена вбудованою: вона може бути визначена однаково у кількох одиницях перекладу, повинна визначатися в кожній одиниці перекладу, в якій вона використовується odr, а поведінка програми немовби існує рівно одна змінна.
та змінено [basic.def] p2:
Декларація - це визначення, якщо
...
- він оголошує статичний член даних поза визначенням класу, і змінна була визначена в класі із специфікатором constexpr (це використання застаріле; див. [depr.static_constexpr]),
...
і додайте [depr.static_constexpr] :
Для сумісності з попередніми C ++ Міжнародними стандартами, статичний член даних constexpr може бути надмірно передекларований за межами класу без ініціалізатора. Це використання застаріло. [Приклад:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
- кінцевий приклад]
C ++ 14 і раніше
У C ++ 03 нам було дозволено надати ініціалізатори в класі для const інтегралів або типів перерахування const , в C ++ 11, використовуючи constexpr
це, було поширено на буквальні типи .
У C ++ 11 нам не потрібно давати визначення простору імен для статичного constexpr
члена, якщо він не odr-використовується , ми можемо бачити це в проекті стандартного розділу C ++ 11 9.4.2
[class.static.data], який говорить ( наголос минає вперед ):
[...] Статичний член даних буквального типу може бути оголошений у визначенні класу за допомогою специфікатора constexpr; якщо так, то його декларація повинна вказувати дужок або рівний ініціалізатор, у якому кожен ініціалізатор-застереження, яке є призначенням-виразом, є постійним виразом. [Примітка. В обох цих випадках член може відображатися в постійних виразах. —Закінчити примітку]
Член все одно повинен бути визначений в області простору імен, якщо він використовується у програмі (3.2), а визначення області простору імен не повинно містити ініціалізатор.
Тоді питання стає, чи baz
використовується тут odr :
std::string str(baz);
і відповідь - так , і тому ми також потребуємо визначення області простору імен.
Тож як ми можемо визначити, чи застосовується змінна odr ? Оригінальне формулювання C ++ 11 у розділі 3.2
[basic.def.odr] говорить:
Експресія потенційно оцінюється, за винятком випадків, коли це неоцінений операнд (п. 5) або його субдекспресія. Змінна, чиє ім'я відображається як потенційно оцінене вираження, застосовується odr, за винятком випадків, коли
це об'єкт, який задовольняє вимогам до появи в постійному виразі (5.19), і негайно застосовується перетворення lvalue в rvalue (4.1) .
Таким baz
чином, дається постійний вираз, але перетворення lvalue в rvalue не застосовується негайно, оскільки воно не застосовується через baz
масив. Про це йдеться у розділі 4.1
[conv.lval], який говорить:
Glvalue (3.10) нефункціонального типу без масиву T може бути перетворений у перше значення.53 [...]
Що застосовується при перетворенні масив-вказівник .
Це формулювання [basic.def.odr] було змінено завдяки Звіту про дефекти 712, оскільки деякі випадки не були охоплені цим формулюванням, але ці зміни не змінюють результатів для цього випадку.