Статический экземпляр элемента шаблона С++
#include <map>
#include <iostream>
template <typename T>
class A
{
static std::map<int, int> data;
public:
A()
{
std::cout << data.size() << std::endl;
data[3] = 4;
}
};
template <typename T>
std::map<int, int> A<T>::data;
//std::map<int, int> A<char>::data;
A<char> a;
int main()
{
return 0;
}
Что не так с этим? Без явного инстанцирования он разбивается на
data[3] = 4;
Явное создание экземпляра решает проблему, но программа прерывается после
std::cout << data.size() << std::endl;
, что означает, что был создан экземпляр шаблона статического класса
data
.
Ответы
Ответ 1
В вашем коде нет явного экземпляра.
Нет порядка инициализации экземпляров статических данных из других статических элементов данных. Таким образом, ваш код имеет эффективное поведение undefined: в зависимости от того, инициализирует ли компилятор карту или a
, ссылка на карту действительна или нет.
См. Инициализация статического члена С++.
Ответ 2
У меня нет Visual С++, но я вижу ту же проблему с компиляцией кода с GCC. Вам необходимо инициализировать элемент данных:
template<> std::map<int, int> A<char>::data = std::map<int, int>();
С этим изменением он компилируется и работает правильно (для меня в GCC на Linux).
Ответ 3
В этом коде есть несколько ошибок. Сначала первоначальная идея не очень хорошая. У вас есть два глобальных статических объекта: a
и A::data
. Порядок их инициализации - undefined. В зависимости от настроения вашего компилятора у вас есть 50% -ный шанс сначала создать конструктор a
и попытаться записать что-то в неинициализированный A::data
.
Это иногда называют проблемой статической инициализации порядка фиаско. Предлагаемое решение состоит в том, чтобы превратить такие объекты в локальные статические объекты, переместив их в функции:
#include <map>
#include <iostream>
template <typename T>
class A
{
std::map<int, int> &data()
{
static std::map<int, int> d;
return d;
}
public:
A()
{
std::cout << data().size() << std::endl;
data()[3] = 4;
}
};
int main()
{
A<char> a;
return 0;
}
Локальные статические объекты инициализируются при первом вызове функции.
О прокомментированном "явном экземпляре" вы забыли template <>
.
Но после того, как вы префикс этой строки с template <>
, это еще не определение, а объявление. Он объявляет, что определение A:: data в другом месте. Чтобы определить его, вам нужно что-то инициализировать, см., Например, ответ Джека Ллойда.