Pytanie Implementacja statycznego elementu szablonu C ++


#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;
}

Co jest z tym nie tak? Bez jawnej instancji to się załamuje

 dane [3] = 4; 
 Jawne tworzenie instancji rozwiązuje problem, ale program ulega przerwaniu po
std :: cout << data.size () << std :: endl;
 co oznacza, że ​​memeber szablonu statycznego klasy data został utworzony.


12
2017-09-13 22:44


pochodzenie


Jaki kompilator? Nie sądzę, że to twoja wina. - Potatoswatter
Kompiluje to dobrze przy użyciu VS2010. - linuxuser27
Używam vs2008 i rzeczywiście kompiluje, ale program łamie na linii danych [3] = 4 - mrs
@mrs: Ah, nie patrzyłem wystarczająco dokładnie na twój kod. Nie możesz uzyskać dostępu data[3] dopóki vector został przeskalowany do co najmniej rozmiaru 4. data.push_back(4) z drugiej strony zwiększa rozmiar data i inicjuje nowy element. Tak więc, jeśli działa poprawnie w każdym przypadku po zmianie na data.push_back(), to nie jest problem z kompilatorem. - Potatoswatter
@Potatoswatter Kontener jest mapą, a nie wektorem; zmiana rozmiaru nie jest wymagana. - Jack Lloyd


Odpowiedzi:


W twoim kodzie nie ma jawnej instancji.

Nie ma kolejności inicjowania inicjowanych statycznych elementów danych wśród innych statycznych elementów danych. Twój kod ma więc niezdefiniowane zachowanie: w zależności od tego, czy kompilator najpierw zainicjuje mapę, czy a, odniesienie do mapy jest prawidłowe lub nie.

Widzieć Inicjalizacja elementu statycznego C ++.


3
2017-09-16 10:15



Próbowałem std :: vector zamiast mapy i wszystko działało dobrze bez jawnego tworzenia instancji - myślisz, że to tylko szczęście? - mrs
i to jest zabawne, że w tym przykładzie pierwsza linia w konstruktorze std :: cout << data.size () << std :: endl; działa zgodnie z oczekiwaniami; program się psuje, gdy próbuję wstawić coś na mapę - mrs
Ta "jawna instancja" nie jest jawną instancją. Jest to definicja statycznego elementu danych o nazwie datawyraźnej specjalizacji szablonu A dla T = char. Nie ma takiej wyraźnej specjalizacji. Kompilator musi wysłać komunikat o błędzie dla tego kodu (jeśli go skomentujesz). - Johannes Schaub - litb


Nie mam pod ręką Visual C ++, ale widzę ten sam problem z twoim kodem kompilującym się z GCC. Musisz zainicjować element danych:

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

Z tą zmianą kompiluje się i działa poprawnie (dla mnie na GCC na Linuksie).


2
2017-09-14 16:56



Jest to potrzebne, ponieważ dla każdego szablonu inicjalizowanego za pomocą innego typu, w tym znaku, potrzebna jest osobna instancja danych - Poorna


W tym kodzie jest kilka błędów. Najpierw pomysł początkowy nie jest dobry. Masz dwa globalne obiekty statyczne: a i A::data. Kolejność, w jakiej są inicjowane, jest niezdefiniowana. W zależności od nastroju kompilatora masz 50% szansy na zbudowanie konstruktora a nazywane jako pierwsze i próbowało zapisać coś do niezainicjowanego A::data.

To się czasami nazywa static fiasco zamówienia początkowego problem. Sugerowanym rozwiązaniem jest przekształcenie takich obiektów w lokalne obiekty statyczne poprzez przeniesienie ich do funkcji:

#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;
}

Lokalne obiekty statyczne są inicjalizowane przy pierwszym wywołaniu funkcji.

O skomentowanej "jawnej instancji", o której zapomniałeś template <>.

Ale po tym przedrostku tej linii template <> nadal nie jest definicja ale a deklaracja. Deklaruje, że istnieje A :: definicja danych gdzieś indziej. Aby ją zdefiniować, musisz ją zainicjować, patrz na przykład odpowiedź Jacka Lloyda.


1
2018-06-25 20:44