Я хотел бы иметь шаблонный класс со статическим членом данных и инициализировать его, эмулируя «статический конструктор». Для класса без шаблонов на этот вопрос уже дан ответ (см. статические конструкторы в C ++? Мне нужно инициализировать частные статические объекты а также Что такое статический конструктор?). Тем не менее, ни один из ответов, похоже, не работает для шаблонного класса.
Ниже приведен пример, который пытается адаптировать идиому «статический конструктор» из предыдущих ответов к шаблонному классу. (Обратите внимание, что пример просто инициализирует int
и может быть написано без таких конструкторов; однако мне нужно общее решение.)
#include <iostream>
struct Foo
{
static int x;
static struct init
{
init()
{
std::cout << "Initializing Foo..." << std::endl;
x = 1;
}
} initializer;
};
int Foo::x;
Foo::init Foo::initializer;
template<int N>
struct Bar
{
static int x;
static struct init
{
init()
{
std::cout << "Initializing Bar..." << std::endl;
x = N;
}
} initializer;
};
template<int N>
int Bar<N>::x;
template<int N>
typename Bar<N>::init Bar<N>::initializer;
int main()
{
std::cout << Foo::x << std::endl;
std::cout << Bar<1>::x << std::endl;
return 0;
}
Это выводит:
Initializing Foo...
1
0
Но я ожидал, что это выведет:
Initializing Foo...
Initializing Bar...
1
1
Это пример «статического фиаско порядка инициализации?»
Нет, это не статический порядок инициализации фиаско. Это просто результат того факта, что каждый член класса шаблона сам по себе является шаблоном и как таковой не создается до момента его использования.
Ваш код никогда не использует init
член, так init
никогда не создается.
Тем не менее, ваша проблема легко решается:
#include <iostream>
template<int N>
struct Bar
{
static int x;
};
template<int N>
int Bar<N>::x= N;
int main()
{
std::cout << Bar<1>::x << std::endl;
return 0;
}
Это дает вам то, что вы хотите проще.
Вы должны явно создать экземпляр initializer
:
[...]
template<int N>
typename Bar<N>::init Bar<N>::initializer;
template
typename Bar<1>::init Bar<1>::initializer;
int main()
{
std::cout << Foo::x << std::endl;
std::cout << Bar<1>::x << std::endl;
return 0;
}
Причина в том, что Bar<1>::x
не зависит от Bar<1>::initializer
, Таким образом, компилятор не создает его, поскольку вы его не используете. Actualy, initializer
инициализация не инициализирует x
, x
первый инициализированный ноль, тогда если initializer
инстанцируется, x
является назначенный новое значение.
Нет риска статического провала инициализации до тех пор, пока initializer
создается в той же единице перевода, что и та, где x
является инстанцирован. Так что это, безусловно, хорошая идея, чтобы явно создать экземпляр x
тоже.
В качестве альтернативы вы можете объявить эти переменные как статические локальные:
#include <iostream>
template<int N>
struct Bar
{
static int x()
{
static int x_val;
static struct init
{
init()
{
std::cout << "Initializing Bar..." << std::endl;
x_val = N;
}
} initializer;//indeed this circumvolution is no more needed.
return x_val;
}
};
int main(){
std::cout << Bar<1>::x() << std::endl;
}
Но если инициализация не тривиальна, сгенерированный код внутри x()
может быть недостаточно оптимизирован.
В зависимости от вашей проблемы, вы также можете определить x как оболочку для int:
class int_inited{
int val;
public:
int_inited(){
std::cout << "Perfoming initialization" << std::endl;
val=42;
}
operator int&(){
return val;
}
operator const int &() const{
return val;
}
};
template<class N>
struct Bar{
static int_inited x;
[...];
Я нашел чистое решение, которое работает для любого типа данных. Поскольку операция присваивания внутри template
оценивается, когда компилятор сталкивается с конкретным Bar<N>::x
чтобы создать экземпляр, мы можем написать:
template<int N>
int Bar<N>::x = init<N>();
где init()
это функция на основе N
который возвращает int
, Дополнительно, init()
будет вызываться только один раз для каждого значения N
который создает экземпляр компилятор.
В качестве более полезного примера здесь я инициализирую статический std::array
согласно некоторой произвольной функции:
#include <iostream>
#include <array>
template<int N>
struct Foo
{
static std::array<double,N> x;
};
template<int N>
std::array<double,N> init()
{
std::array<double,N> y;
for (int i=0; i<N; ++i) {
y[i] = (double)(i*i+i)/N;
}
return y;
}
template<int N>
std::array<double,N> Foo<N>::x = init<N>();
int main()
{
const int N = 10;
for (int i=0; i<N; ++i) {
std::cout << Foo<N>::x[i] << std::endl;
}
return 0;
}