Pergunta

Eu tenho uma classe de modelo definido em um arquivo de cabeçalho como este. Aqui eu ter definido uma variável estática, bem como:

#ifndef TEST1_H_
#define TEST1_H_

void f1();

static int count;

template <class T>
class MyClass
{
public:

    void f()
    {
        ++count;
    }


};

#endif

E eu ter definido a função main () em um arquivo cpp diferente como esta:

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

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

    return 0;
}

Eu tenho implementado função f1 () em um arquivo cpp diferente como esta:

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

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

Quando eu compilei esta usando VC6, eu tenho a saída como "F1: 0 principal: 2". Como isso é possível? Além disso, em geral, como devo lidar com se eu quiser usar variáveis ??estáticas juntamente com templates?

Foi útil?

Solução

Você está recebendo duas cópias da mesma variável porque você declarou uma variável estática em um arquivo de cabeçalho. Quando você declarar um static variável global dessa maneira, você está dizendo que é local para a unidade de compilação (o arquivo .o). Desde que você incluir o cabeçalho em duas unidades de compilação, você tem duas cópias de count.

Eu acho que você realmente quer aqui é uma variável de membro de modelo estático associado a cada exemplo da classe de modelo. Ele ficaria assim:

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

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

Isso vai te dar uma contagem para cada instanciação de seu modelo. Ou seja, você terá uma contagem para MyClass<int>, MyClass<foo>, MyClass<bar>, etc. f1() iria ficar assim:

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

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

Se você quer uma contagem para todas instâncias de MyClass (independentemente de seus parâmetros de modelo), você precisa usar um variável global .

No entanto, você provavelmente não quer uma variável global diretamente, porque você corre o risco de usá-lo antes que ele chegue inicializado. Você pode contornar isso através de um método estático global que retorna uma referência para a contagem:

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

Em seguida, acessá-lo a partir de dentro de sua classe como este:

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

Isto irá assegurar que a contagem é inicializado antes de ser usado, independentemente de qual unidade de compilação que você acessá-lo a partir. Veja a C ++ FAQ sobre estática inicialização Para mais detalhes .

Outras dicas

Colocar a declaração estática em um arquivo de cabeçalho fará com que cada arquivo .cpp para obter a sua própria versão da variável. Assim, as duas declarações cout está imprimindo variáveis ??diferentes.

Você esperava "F1: 1 Main: 1"? Você instanciado MyClass<int> em duas unidades de tradução separados (ou seja, dois arquivos de objetos), ea serra vinculador que havia uma instanciação de modelo duplicado, por isso descartou a instanciação que estava no arquivo objeto de f1.

Você está passando /OPT:ICF ou /OPT:REF para o vinculador VC6? Isso pode estar relacionado com o duplicado remoção instanciação de modelo (ou não; duplicados instantiations modelo pode ser um caso especial, em comparação com funções duplicadas comuns). GCC parece fazer algo semelhante em algumas plataformas.

De qualquer forma, eu não iria contar com esse comportamento ser consistente em compiladores. Além disso, alterar a ordem dos arquivos de objetos na linha de comando vinculador pode afetar o que instanciação fica descartada.

Não há outra solução, você pode criar uma classe pai comum e colocar essa variável estática na mesma, em seguida, fazer a sua classe de modelo herdá-la de forma privada, aqui está um exemplo:

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

saída será:

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

Eu acho que isso é realmente comportamento indefinido .

De acordo com C ++ 14 [basic.def.odr] / 6:

Não pode haver mais de uma definição de um [...] função membro de um modelo de classe [...] em um programa, desde que cada um aparece definição em uma unidade de tradução diferente, e desde que as definições satisfazer os seguintes requisitos . Dada tal D entidade nomeada definida em mais de uma unidade de tradução, então

  • cada definição de D deve ser constituído da mesma sequência de símbolos; e
  • em cada definição de D, nomes correspondentes, olhou-se de acordo com 3.4, deve referir-se a uma entidade definido na definição de D, ou refere-se a uma mesma pessoa, depois de resolução de sobrecarga (13.3) e depois de correspondência de modelo parcial especialização (14.8.3), exceto que um nome pode se referir a um não-volátil objecto constante com ligação interna ou não se o objecto tem o mesmo tipo literal em todas as definições de D, e o objecto é inicializado com uma expressão constante (5,19), e o objecto não é ODR-utilizados, e o objecto tem o mesmo valor em todas as definições de D; [...]

O problema é que, no primeiro arquivo .cpp, o count nome dentro f1 refere-se a um objeto diferente do count nome dentro f1 no segundo arquivo .cpp, violando assim a condição de que os nomes correspondentes devem se referir à mesma entidade.

Eles são objetos diferentes por causa do especificador static que diz que cada unidade de tradução recebe o seu próprio objeto com esse nome.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top