Korzystanie ze zmiennej statycznej wraz z szablonami

Mam klasę szablonów zdefiniowaną w pliku nagłówkowym, takim jak ten. Tutaj zdefiniowałem również zmienną statyczną:

#ifndef TEST1_H_
#define TEST1_H_

void f1();

static int count;

template <class T>
class MyClass
{
public:

    void f()
    {
        ++count;
    }


};

#endif

I zdefiniowałem funkcję main () w innym pliku cpp, jak to:

int main(int argc, char* argv[])
{
    MyClass<int> a;
    a.f();
    f1();

    cout<<"Main:" << count << "\n";

    return 0;
}

Zaimplementowałem funkcję f1 () w innym pliku cpp, takim jak:

void f1()
{
    MyClass<int> a;
    a.f();

    cout<<"F1: " <<count <<"\n";
}

Kiedy skompilowałem to używając VC6, otrzymałem wyjście jako "F1: 0 Main: 2". Jak to możliwe? Ponadto, ogólnie jak powinienem sobie radzić, jeśli chcę używać zmiennych statycznych wraz z szablonami?

Author: EvilTeach, 2009-03-03

5 answers

Otrzymujesz dwie kopie tej samej zmiennej, ponieważ zadeklarowałeś zmienną statyczną w pliku nagłówkowym. Kiedy deklarujesz zmienną globalną static w ten sposób, mówisz, że jest ona lokalna dla jednostki kompilacji (pliku .o). Ponieważ nagłówek zawiera się w dwóch jednostkach kompilacji, otrzymuje się dwie kopie count.

Myślę, że to, czego naprawdę chcesz, to statyczna zmienna szablonu powiązana z każdą instancją klasy szablonu. Wyglądałoby to jak to:

template <class T>
class MyClass
{
    // static member declaration
    static int count;
    ...
};

// static member definition
template<class T> int MyClass<T>::count = 0;

To da ci liczbę dla każdej instancji Twojego szablonu. Oznacza to, że będziesz musiał liczyć na MyClass<int>, MyClass<foo>, MyClass<bar>, itd. f1() Teraz wyglądałoby tak:

void f1() {
    MyClass<int> a;
    a.f();

    cout<<"F1: " << MyClass<int>::count <<"\n";
}

Jeśli chcesz mieć liczbę dla wszystkich instancji MyClass (niezależnie od ich parametrów szablonu), musisz użyć zmiennej globalnej .

Jednak prawdopodobnie nie chcesz zmiennej globalnej bezpośrednio, ponieważ ryzykujesz jej użycie, zanim się zainicjowany. Możesz to obejść, tworząc globalną metodę statyczną, która zwraca odniesienie do Twojej liczby:

int& my_count() {
    static int count = 0;
    return count;
}

Następnie dostęp do niego z klasy w następujący sposób:

void f() {
    ++my_count();
}

Zapewni to, że licznik zostanie zainicjowany przed jego użyciem, niezależnie od tego, z której jednostki kompilacji uzyskasz do niego dostęp. Więcej informacji można znaleźć w C++ FAQ na temat statycznej kolejności inicjalizacji.

 22
Author: tgamblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-07 19:31:29

Umieszczenie statycznej deklaracji w pliku nagłówkowym spowoduje, że każdyplik cpp, aby uzyskać własną wersję zmiennej. Więc dwa polecenia cout wypisują różne zmienne.

 3
Author: David Norman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-03 17:11:27

Spodziewałeś się "F1:1 Main: 1"? Utworzono instancję MyClass<int> w dwóch oddzielnych jednostkach translacyjnych (tj. dwóch plikach obiektowych), a linker zauważył, że istnieje zduplikowana instancja szablonu, więc odrzucił instancję znajdującą się w pliku obiektowym f1.

Are you passing /OPT:ICF lub /OPT:REF do łącznika VC6? Może to być związane z usunięciem duplicate template instantiation (lub nie; duplicate template instantiations może być szczególnym przypadkiem, w porównaniu ze zwykłym duplicate functions). GCC wydaje się robić coś podobnego na niektórych platformach.

W każdym razie, nie liczyłbym na to, że to zachowanie będzie spójne w kompilatorach. Zmiana kolejności plików obiektowych w wierszu poleceń linkera może mieć wpływ na to, która instancja zostanie odrzucona.

 1
Author: bk1e,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 07:07:30

Jest inne rozwiązanie, możesz utworzyć współdzieloną klasę rodzica i umieścić w niej zmienną statyczną, a następnie sprawić, by klasa szablonu dziedziczyła ją prywatnie, oto przykład:

class Parent
{
protected: 
    static long count;
};

long Parent::count = 0;

template<typename T>
class TemplateClass: private Parent
{
private: 
    int mKey;
public:
    TemplateClass():mKey(count++){}
    long getKey(){return mKey;}
}

int main()
{
    TemplateClass<int> obj1;
    TemplateClass<double> obj2;

    std::cout<<"Object 1 key is: "<<obj1.getKey()<<std::endl;
    std::cout<<"Object 2 key is: "<<obj2.getKey()<<std::endl;

    return 0;
}

Wyjście będzie:

Object 1 key is: 0 
Object 2 key is: 1
 0
Author: POSTHUMAN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-18 15:58:57

Myślę, że to jest rzeczywiście nieokreślone zachowanie.

Zgodnie z C++14 [basic.def.odr] / 6:

Może być więcej niż jedna definicja [...] funkcja członka szablonu klasy [...] w programie pod warunkiem, że każda definicja pojawia się w innej jednostce tłumaczeniowej i pod warunkiem, że definicje spełniają następujące wymagania. Biorąc pod uwagę taki byt o nazwie D zdefiniowany w więcej niż jednej jednostce translacyjnej, to

  • każda definicja D składa się z tej samej sekwencji żetonów; oraz
  • w każdej definicji D odpowiednie nazwy, wyszukane zgodnie z 3.4, odnoszą się do podmiotu zdefiniowanego w definicji D, lub odnoszą się do tego samego podmiotu, po rozwiązaniu przeciążenia (13.3) i po dopasowaniu częściowej specjalizacji szablonu (14.8.3), z tym wyjątkiem, że nazwa może odnosić się do nieulotnego podmiotu. const obiekt z wewnętrznym lub bez powiązania, jeśli obiekt ma ten sam typ literalny we wszystkich definicjach D, A obiekt jest inicjalizowany wyrażeniem stałym (5.19), a obiekt nie jest używany odr, a obiekt ma tę samą wartość we wszystkich definicjach D; [...]

Problem polega na tym, że w pierwszym pliku .cpp Nazwa count wewnątrz f1 odnosi się do innego obiektu niż nazwa count wewnątrz f1 w drugim pliku .cpp, naruszając tym samym warunek, że odpowiednie nazwy powinny odnosić się do tego samego obiektu.

Są różnymi obiektami ze względu na static specifier, który mówi, że każda jednostka tłumaczenia dostaje swój własny obiekt o tej nazwie.

 -1
Author: M.M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-16 10:26:46