Инициализация статического члена внутри шаблона

Вот минимальный пример:

#include <iostream>

struct B {
B() { x = 42; }
static int x;
};

int B::x;

template <int N>
struct A {
int foo() { return b.x; }
static B b;
};

template<int N>
B A<N>::b;

//template struct A<2>; // explicit instantiation with N = 2 (!)

int main(int argc, char **argv) {
std::cout << A<1>().foo() << std::endl;
return 0;
}

Эта программа записывает 42 с использованием g ++ 4.9.2, но записывает 0 с использованием Visual Studio 2015 RC. Кроме того, если я раскомментирую явное создание экземпляра, VS2015RC также выдаст 42, что довольно интересно, так как параметр шаблона здесь отличается от того, который используется в main функция.

Это ошибка? Я предполагаю, что g ++ является правильным, так как есть ссылка на b внутри foo, так Bконструктор должен быть вызван.


РЕДАКТИРОВАТЬ: Существует простой обходной путь — если есть нестатическая переменная в B, что упоминается в A, VS2015RC скомпилирует правильно:

// ...

struct B {
B() { x = 42; }
static int x;
int y;                         // <- non-static variable
};

// ...

template <int N>
struct A {
int foo() { b.y; return b.x; } // <- reference to b.y
static B b;
};

Кажется, это работает, хотя b.y, как утверждение, очевидно, NOP.

6

Решение

Из [basic.start.init]:

Переменные со статической продолжительностью хранения (3.7.1) или продолжительностью хранения потока (3.7.2) должны быть инициализированы нулями (8.5)
до любой другой инициализации. Инициализатор констант для объекта o — это выражение, которое является
константное выражение, за исключением того, что оно может также вызывать конструкторы constexpr для o и его подобъектов даже
если эти объекты не литеральных типов классов. […]

Вместе нулевая инициализация и постоянная инициализация называются статической инициализацией; все остальные инициализации
динамическая инициализация. Статическая инициализация должна быть выполнена до любой динамической инициализации.

В нашем случае b статически инициализируется, но b.x динамически инициализируется (конструктор не является constexpr). Но у нас также есть:

Это определяется реализацией, является ли динамическая инициализация нелокальной переменной статическим хранилищем
Продолжительность делается до первого утверждения основного. Если инициализация откладывается до некоторого момента времени
после первого оператора main это должно произойти до первого использования odr (3.2) любой функции или переменной
определяется в той же единице перевода, что и инициализируемая переменная.

Odr-используемые средства от [basic.def.odr]:

Переменная x, имя которой появляется в качестве потенциально оцениваемого выражения ex, используется ex odr, если не применяется
преобразование lvalue-to-rvalue (4.1) в x дает постоянное выражение (5.20), которое не вызывает никаких нетривиальных
функции и, если […]

Но оценивая b.x не дает постоянного выражения, поэтому мы можем на этом остановиться — b.x является УСО используемый от A<N>::foo(), который также является первым УСО использование. Так что пока инициализация не должна происходить раньше main(), это должно произойти до foo(), Так что, если вы получите 0, это ошибка компилятора.

6

Другие решения

Я был бы склонен написать код так:

struct B {
B() {}
static int x;
};

int B::x = 42;

В конце концов, static (x) определен (и, следовательно, должен быть инициализирован) в последней строке. Помещение инициализации внутри конструктора B означает, что статический x (есть только один из них!) Будет переинициализироваться каждый раз, когда вы создаете B. Существует одна статическая, вы должны инициализировать только один раз.

-2

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector