const float
не соответствует требованиям для того, чтобы быть константным выражением
(Этот ответ основан на @ комментарии Окталиста , поскольку он воздержалсяпревращения в собственный ответ)
В следующем:
// foo.h
struct Foo {
static const float kValue;
};
// foo.cpp
const float Foo::kValue = 1.5F;
Foo::kValue
действительно инициализируется константным выражением посредством постоянной инициализации , но Foo::kValue
не является самим постоянным выражением, поскольку оно не является ни целым, ни перечислением, constexpr, ни временным. [expr.const] / 2 состояния [ выделение шахта]:
A условное выражение является основным константным выражением , если оно не включает одно из следующих значений в качестве потенциально вычисляемого подвыражения ( [basic.def.odr] ), но подвыражения логического И( [expr.log.and] ), логическое ИЛИ ( [expr.log.or] ) и условные ( [expr.cond] ) операциикоторые не оцениваются, не считаются [Примечание: перегруженный оператор вызывает функцию.- примечание к концу]:
...
(2.9) : преобразование lvalue в rvalue ( [conv.lval] ) , если только он не применяется к
- glvalue целочисленного или перечислимого типа, который относится к энергонезависимому константному объекту с предшествующей инициализацией, инициализированному с помощьюконстантное выражение, или
- glvalue литерального типа, который ссылается на энергонезависимый объект, определенный с constexpr, или который ссылается на подобъект такого объекта, или
- glvalueлитеральный тип, который относится к энергонезависимому временному объекту, время жизни которого не закончилось, инициализируется постоянным выражением;
Как ни один из подпунктов для (2.9) *Здесь применяется 1070 *, Foo::kValue
не является константным выражением.Из [basic.start.init] / 2 (как указано в более ранней стандартной версии вопроса) Bar::kValue
не инициализируется с помощью постоянной инициализации , нокак часть динамической инициализации .
Переменные со статической продолжительностью хранения ( [basic.stc.static] ) или продолжительностью хранения потока ( [basic.stc.thread] ) должен быть инициализирован нулями ( [dcl.init] ) перед любой другой инициализацией [ выделяется моя]:
Инициализация константы выполняется:
- ...
- , если объект со статическим или потоковым хранением не был инициализирован вызовом конструктора и если каждое полное выражение, которое появляется в его инициализаторе, является константным выражением .
Обратите внимание, что этот конкретный пример не приводит к риску статического порядка инициализации fiasco, поскольку Foo::kValue
инициализируется как средство постоянной инициализации, а Bar::kValue
инициализируется как часть динамической инициализации, и первый гарантированно завершится до начала динамической инициализации.
Если первый также будетбыть инициализирован как часть динамической инициализации, инициализация этих двух будет неопределенно упорядочена относительно друг друга (и всех других динамических инициализаций).
Никогда не полагайтесь на факт, однако, что этот конкретный пример имеетчетко определенный порядок инициализации, так как незначительные изменения могут сделать недействительным этот факт:
// foo.h
struct Foo {
static const float kDummyValue;
static const float kValue;
};
// foo.cpp
const float Foo::kDummyValue = 1.5F; // Constant initialization
const float Foo::kValue = kDummyValue; // (!) Dynamic initialization
// bar.h
struct Bar {
static const float kValue;
};
// bar.cpp
#include "foo.h"
const float Bar::kValue = Foo::kValue; // (!) Dynamic initialization
// main.cpp
#include "bar.h"
#include <iostream>
int main() { std::cout << Bar::kValue; }
Как и в этом примере модификатора, инициализация Foo::kValue
и Bar::kValue
последовательно определяется друг относительно друга, что означает Bar::kValue
можно инициализировать (со значением "Foo::kValue
) до того, как Foo::kValue
будет.