Гарантии порядка инициализации для встроенного инициализированного статического члена класса const - PullRequest
1 голос
/ 10 октября 2019

Скажем, у меня есть static const int переменная члена класса. Он инициализируется непосредственно в определении класса, но не имеет определения в .cpp (что нормально, поскольку он не используется odr).

Далее, скажем, эта константа используется в конструкторесписок инициализаторов другого класса, и создается глобальный экземпляр этого другого класса.

// mytype1.hpp
class MyType1
{
public:
    static const int g_myConstant = 42; // no definition in cpp
};

// mytype2.hpp
class MyType2
{
public:
    MyType2();
private:
    int m_myMember;
};

// mytype2.cpp
MyType2::MyType2()
    : m_myMember(MyType1::g_myConstant)
{
}

// otherfile.cpp
// is the reference to MyType1::g_myConstant in the ctor well defined?
MyType2 myType2GlobalInstance;

Хорошо ли определена конструкция myType2GlobalInstance? Другими словами: Какие гарантии дает C ++ для статического порядка инициализации static const переменных-членов класса?

Поскольку нет определения этой константы, вероятно, для нее нет памяти, которую нужно инициализировать, ипеременная ведет себя больше как макрос препроцессора .. но гарантировано ли это? Имеет ли это значение, если константа определена или нет?

Изменится ли что-нибудь, если переменная-член будет static constexpr?

1 Ответ

1 голос
/ 10 октября 2019

Для статической инициализации, до C ++ 14, нулевая инициализация происходит до постоянной инициализации. Начиная с C ++ 14, постоянная инициализация происходит вместо нулевой инициализации. Инициализация констант в основном происходит для объектов и ссылок, которые инициализируются с помощью константных выражений (или конструкторов constexpr). Подробности здесь .

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

m_myMember не является статической инициализированной, потому что его конструктор класса не constexpr. g_myConstant константа инициализирована. Использование static constexpr заставляет компилятор инициализировать значение во время компиляции, без constexpr компилятор может инициализировать во время компиляции. Ваша конструкция четко определена.

Посмотрите этот пример из cppreference :

#include <iostream>
#include <array>

struct S {
    static const int c;
};
const int d = 10 * S::c; // not a constant expression: S::c has no preceding
                         // initializer, this initialization happens after const
const int S::c = 5;      // constant initialization, guaranteed to happen first
int main()
{
    std::cout << "d = " << d << '\n';
    std::array<int, S::c> a1; // OK: S::c is a constant expression
//  std::array<int, d> a2;    // error: d is not a constant expression
}

Но теперь, если мы изменим порядок инициализации, он скомпилирует:

#include <iostream>
#include <array>

struct S {
    static const int c;
};
//both inits are now constant initialization
const int S::c = 5;
const int d = 10 * S::c;
int main()
{
    std::cout << "d = " << d << '\n';
    std::array<int, S::c> a1;
    std::array<int, d> a2; //now, it's ok
}
...