undefined reference to membro da classe estática
Pergunta
Alguém pode explicar por que seguinte código não irá compilar? Pelo menos no g ++ 4.2.4.
E mais interessante, porque ele irá compilar quando eu Membro do elenco para int?
#include <vector>
class Foo {
public:
static const int MEMBER = 1;
};
int main(){
vector<int> v;
v.push_back( Foo::MEMBER ); // undefined reference to `Foo::MEMBER'
v.push_back( (int) Foo::MEMBER ); // OK
return 0;
}
Solução
Você precisa realmente definir a algum lugar membro estático (após a definição de classe). Tente isto:
class Foo { /* ... */ };
const int Foo::MEMBER;
int main() { /* ... */ }
Isso deve se livrar da referência indefinida.
Outras dicas
O problema surge por causa de um confronto interessante de novos recursos C ++ e que você está tentando fazer. Primeiro, vamos dar uma olhada na assinatura push_back
:
void push_back(const T&)
Ele está esperando uma referência a um objeto do tipo T
. Sob o antigo sistema de inicialização, existe um tal membro. Por exemplo, o seguinte código compila muito bem:
#include <vector>
class Foo {
public:
static const int MEMBER;
};
const int Foo::MEMBER = 1;
int main(){
std::vector<int> v;
v.push_back( Foo::MEMBER ); // undefined reference to `Foo::MEMBER'
v.push_back( (int) Foo::MEMBER ); // OK
return 0;
}
Isso ocorre porque há um algum lugar objeto real que tem esse valor armazenado nele. Se, no entanto, pode mudar para o novo método de especificar membros const estáticas, como você tem acima, Foo::MEMBER
não é mais um objeto. É uma constante, um pouco parecido com:
#define MEMBER 1
Mas, sem as dores de cabeça de uma macro pré-processador (e com segurança de tipo). Isso significa que o vetor, o que está esperando uma referência, não podem obter um.
O padrão C ++ exige uma definição para o seu membro const estático se a definição é de alguma forma necessária.
A definição é necessária, por exemplo, se o endereço é utilizado. push_back
leva o seu parâmetro por referência const, e tão estritamente o compilador precisa do endereço de seu membro e você precisa defini-lo no espaço de nomes.
Quando você converter explicitamente a constante, você está criando um temporário e é este temporário, que está vinculado ao de referência (sob regras especiais no padrão).
Este é um caso muito interessante, e eu realmente acho que vale a pena levantar uma questão de modo que o DST ser alterado para ter o mesmo comportamento para o seu membro constante!
Embora, em um tipo estranho de maneira isso poderia ser visto como um uso legítimo do operador unário '+'. Basicamente o resultado da unary +
é um rvalue e assim as regras para a ligação do rvalues ??para referências const aplicar e nós não usamos o endereço do nosso membro const estática:
v.push_back( +Foo::MEMBER );
Aaa.h
class Aaa {
protected:
static Aaa *defaultAaa;
};
Aaa.cpp
// You must define an actual variable in your program for the static members of the classes
static Aaa *Aaa::defaultAaa;
Não faço ideia por que as obras do elenco, mas Foo :: MEMBRO não é alocado até a primeira vez Foo é carregado, e uma vez que você nunca carregá-lo, nunca é alocado. Se você tivesse uma referência a um lugar Foo, ele provavelmente iria funcionar.
Quanto à segunda pergunta: push_ref leva de referência como um parâmetro, e você não pode ter uma referência para memeber const estático de uma classe / struct. Uma vez que você chamar static_cast, uma variável temporário é criado. E uma referência para este objeto pode ser passado, tudo funciona muito bem.
Ou, pelo menos, o meu colega que resolveu este disse isso.
Com C ++ 11, o acima seria possível para tipos básicos como
class Foo {
public:
static constexpr int MEMBER = 1;
};
A parte constexpr
cria uma estática expressão em oposição a um estático variável - e que se comporta exatamente como uma definição de método em linha extremamente simples. A abordagem mostrou um pouco vacilante com constexprs C-corda dentro de classes de modelo, no entanto.