Sempre considerando que o seguinte cabeçalho, contendo minha classe modelada, está incluído em pelo menos dois .CPP
arquivos, este código é compilado corretamente:
template <class T>
class TClass
{
public:
void doSomething(std::vector<T> * v);
};
template <class T>
void TClass<T>::doSomething(std::vector<T> * v) {
// Do something with a vector of a generic T
}
template <>
inline void TClass<int>::doSomething(std::vector<int> * v) {
// Do something with a vector of int's
}
Mas observe o inline no método de especialização. É necessário para evitar um erro de vinculador (em VS2008 é LNK2005) devido ao método ser definido mais de uma vez. Eu entendo isso porque AFAIK uma especialização de modelo completo é o mesmo que uma definição de método simples.
Então, como faço para remover isso inline
? O código não deve ser duplicado em cada uso dele. Eu pesquisei no Google, li algumas perguntas aqui no SO e tentei muitas das soluções sugeridas, mas nenhuma construída com sucesso (pelo menos não no VS 2008).
Obrigado!
Respostas:
Assim como acontece com funções simples, você pode usar declaração e implementação. Coloque sua declaração de cabeçalho:
e coloque a implementação em um de seus arquivos cpp:
Não se esqueça de remover inline (esqueci e pensei que essa solução não funcionaria :)). Verificado em VC ++ 2005
fonte
inline
enquanto copia / cola. Assim funcionou!Você precisa mover a definição de especialização para o arquivo CPP. A especialização da função de membro da classe de modelo é permitida mesmo se a função não for declarada como modelo.
fonte
Não há razão para remover a palavra-chave inline.
Isso não altera o significado do código de forma alguma.
fonte
inline
palavra - chave resultar na função realmente inline (o padrão diz que o compilador deve interpretá-la como uma dica), então essas cópias extras não podem ser removidas. É, no entanto, apenas uma dica para embutir (seu efeito principal é dizer "não gere erros em colisões de links de uma maneira particular")Se você deseja remover o inline por qualquer motivo, a solução de maxim1000 é perfeitamente válida.
Em seu comentário, entretanto, parece que você acredita que a palavra-chave inline significa que a função com todo o seu conteúdo fica sempre inline, mas AFAIK, que na verdade depende muito da otimização do compilador.
Citando o FAQ do C ++
Então, a menos que você saiba que essa função irá realmente inchar seu executável ou a menos que você queira removê-lo do cabeçalho de definição do template por outros motivos, você pode realmente deixá-lo onde está sem nenhum dano
fonte
Gostaria de acrescentar que ainda há um bom motivo para manter a
inline
palavra-chave lá se você pretende deixar também a especialização no arquivo de cabeçalho.Referência: https://stackoverflow.com/a/4445772/1294184
fonte
Este é um pequeno OT, mas pensei em deixar isso aqui para o caso de ajudar alguém. Eu estava pesquisando sobre a especialização de modelos, o que me trouxe até aqui e, embora a resposta de @max1000 esteja correta e, no final das contas, tenha me ajudado a descobrir meus problemas, não achei que fosse muito claro.
Minha situação é um pouco diferente (mas semelhante o suficiente para deixar esta resposta, eu acho) do que os OPs. Basicamente, estou usando uma biblioteca de terceiros com todos os diferentes tipos de classes que definem "tipos de status". O coração desses tipos é simplesmente
enum
s, mas todas as classes herdam de um pai comum (abstrato) e fornecem diferentes funções de utilidade, como sobrecarga de operador e umastatic toString(enum type)
função. Cada statusenum
é diferente um do outro e não está relacionado. Por exemplo, umenum
tem os camposNORMAL, DEGRADED, INOPERABLE
, outro temAVAILBLE, PENDING, MISSING
, etc. Meu software é responsável por gerenciar diferentes tipos de status para diferentes componentes. Acontece que eu queria utilizar astoString
funções para essesenum
classes, mas como são abstratas, não pude instanciá-las diretamente. Eu poderia ter estendido cada classe que quisesse usar, mas no final decidi criar umatemplate
classe, ondetypename
seria qualquer status concreto queenum
me importasse. Provavelmente algum debate pode ser feito sobre essa decisão, mas eu senti que era muito menos trabalhoso do que estender cadaenum
classe abstrata com uma personalizada minha e implementar as funções abstratas. E, claro, no meu código, eu só queria poder chamar.toString(enum type)
e fazer com que ele imprima a representação da string dissoenum
. Uma vez que todos osenum
s eram totalmente não relacionados, cada um deles tinha seu própriotoString
funções que (depois de algumas pesquisas que aprendi) tinham que ser chamadas usando a especialização de modelo. Isso me trouxe aqui. Abaixo está um MCVE do que eu tive que fazer para fazer isso funcionar corretamente. E, na verdade, minha solução foi um pouco diferente da de @ maxim1000.Este é um arquivo de cabeçalho (bastante simplificado) para os
enum
s. Na realidade, cadaenum
aula foi definida em seu próprio arquivo. Este arquivo representa os arquivos de cabeçalho que são fornecidos a mim como parte da biblioteca que estou usando:adicionar esta linha apenas para separar o próximo arquivo em um bloco de código diferente:
próximo arquivo
próximo arquivo
e isso resulta em:
Não tenho ideia se essa é a solução ideal para resolver meu problema, mas funcionou para mim. Agora, não importa quantos tipos de enumeração eu acabe usando, tudo que tenho a fazer é adicionar algumas linhas para o
toString
método no arquivo .cpp, e posso usar otoString
método já definido das bibliotecas sem implementá-lo sozinho e sem estender cada umenum
classe que eu quero usar.fonte