Domanda

Ho una classe di template definita in un file header come questo. Qui ho anche definito una variabile statica:

#ifndef TEST1_H_
#define TEST1_H_

void f1();

static int count;

template <class T>
class MyClass
{
public:

    void f()
    {
        ++count;
    }


};

#endif

E ho definito la funzione main () in un diverso file cpp come questo:

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

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

    return 0;
}

Ho implementato la funzione f1 () in un diverso file cpp come questo:

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

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

Quando l'ho compilato usando VC6, ho ottenuto l'output come " F1: 0 Principale: 2 " ;. Com'è possibile? Inoltre, in generale come devo gestire se desidero utilizzare variabili statiche insieme a modelli?

È stato utile?

Soluzione

Stai ricevendo due copie della stessa variabile perché hai dichiarato una variabile statica in un file di intestazione. Quando dichiari una variabile globale static in questo modo, stai dicendo che è locale all'unità di compilazione (il file .o). Poiché si include l'intestazione in due unità di compilazione, si ottengono due copie di count.

Penso che ciò che vuoi davvero qui sia una variabile membro statica del modello associata ad ogni istanza della classe template. Sembrerebbe così:

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

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

Questo ti farà contare per ogni istanza del tuo modello. Cioè, avrai un conteggio per MyClass<int>, MyClass<foo>, MyClass<bar>, ecc. f1() ora sarebbe simile a questo:

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

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

Se si desidera un conteggio per tutte le istanze di MyClass (indipendentemente dai parametri del modello), è necessario utilizzare una variabile globale .

Tuttavia, probabilmente non vuoi direttamente una variabile globale perché corri il rischio di usarla prima che venga inizializzata. Puoi aggirare questo problema creando un metodo statico globale che restituisce un riferimento al tuo conteggio:

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

Quindi accedendo dall'interno della tua classe in questo modo:

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

Ciò garantirà che il conteggio venga inizializzato prima che venga utilizzato, indipendentemente dall'unità di compilazione a cui si accede. Vedi le FAQ C ++ sull'ordine di inizializzazione statica per maggiori dettagli .

Altri suggerimenti

Inserendo la dichiarazione statica in un file di intestazione, ogni file .cpp otterrà la propria versione della variabile. Quindi le due istruzioni cout stampano variabili diverse.

Ti aspettavi " F1: 1 Principale: 1 " ;? Hai istanziato MyClass<int> in due unità di traduzione separate (ovvero due file oggetto) e il linker ha visto che c'era un'istanza del modello duplicata, quindi ha eliminato l'istanza che era nel file oggetto di f1.

Stai passando /OPT:ICF o /OPT:REF al linker VC6? Ciò potrebbe essere correlato alla rimozione dell'istanza del modello duplicato (oppure no; le istanze del modello duplicato potrebbero essere un caso speciale, rispetto alle normali funzioni duplicate). GCC sembra fare qualcosa di simile su alcune piattaforme.

Comunque, non farei affidamento sul fatto che questo comportamento sia coerente tra i compilatori. Inoltre, la modifica dell'ordine dei file oggetto nella riga di comando del linker potrebbe influire sull'istanza che viene scartata.

Esiste un'altra soluzione, è possibile creare una classe principale condivisa e inserire questa variabile statica in essa, quindi fare in modo che la classe del modello la erediti privatamente, ecco un esempio:

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

L'output sarà:

Object 1 key is: 0 
Object 2 key is: 1

Penso che questo sia in realtà comportamento indefinito .

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

  

In un programma può esistere più di una definizione di una funzione membro [...] di un modello di classe [...] a condizione che ciascuna definizione appaia in un'unità di traduzione diversa e che le definizioni soddisfino i seguenti requisiti . Data tale entità denominata D definita in più di una unità di traduzione, quindi

     
      
  • ogni definizione di D deve consistere nella stessa sequenza di token; e
  •   
  • in ciascuna definizione di D, i nomi corrispondenti, cercati secondo 3.4, devono riferirsi a un'entità definita all'interno della definizione di D, o devono riferirsi alla stessa entità, dopo la risoluzione del sovraccarico (13.3) e dopo la corrispondenza del modello parziale specializzazione (14.8.3), tranne per il fatto che un nome può riferirsi a un non volatile   oggetto const con collegamento interno o assente se l'oggetto ha lo stesso tipo letterale in tutte le definizioni di D e l'oggetto è inizializzato con un'espressione costante (5.19) e l'oggetto non è usato in odr e l'oggetto ha lo stesso valore in tutte le definizioni di D; [...]
  •   

Il problema è che nel primo .cpp file, il nome count in f1 si riferisce a un oggetto diverso dal nome static entro <=> nel secondo <=> file, violando così il condizione che i nomi corrispondenti facciano riferimento alla stessa entità.

Sono oggetti diversi a causa dell'identificatore <=> che dice che ogni unità di traduzione ottiene il proprio oggetto con quel nome.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top