Создание шаблона статического члена C ++ - PullRequest
14 голосов
/ 14 сентября 2010
#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;
, что означает, что экземпляр статического класса memeber data был создан.

Ответы [ 3 ]

4 голосов
/ 16 сентября 2010

Нет явного создания экземпляра в вашем коде.

Нет порядка инициализации инстанцированных элементов статических данных среди других элементов статических данных. Таким образом, ваш код имеет фактически неопределенное поведение: в зависимости от того, будет ли компилятор сначала инициализировать карту или a, ссылка на карту действительна или нет.

См. C ++ Инициализация статического члена .

2 голосов
/ 14 сентября 2010

У меня нет под рукой Visual C ++, но я вижу ту же проблему с вашим кодом, компилируемым с помощью GCC. Вам нужно инициализировать элемент данных:

template<> std::map<int, int> A<char>::data = std::map<int, int>();

С этим изменением он компилируется и работает правильно (для меня на GCC в Linux).

1 голос
/ 25 июня 2015

В этом коде несколько ошибок. Сначала первоначальная идея не очень хорошая. У вас есть два глобальных статических объекта: a и A::data. Порядок, в котором они инициализируются, не определен. В зависимости от настроения вашего компилятора у вас есть 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 где-то еще . Чтобы действительно определить его, вам нужно инициализировать его чем-то, см., Например, ответ Джека Ллойда.

...