Quando eu uso um modelo especializado em arquivos de objeto diferentes, recebo um erro de "definição múltipla" ao vincular. A única solução que encontrei envolve o uso da função "embutida", mas parece apenas uma solução alternativa. Como faço para resolver isso sem usar a palavra-chave "inline"? Se isso não for possível, por quê?
Aqui está o código de exemplo:
paulo@aeris:~/teste/cpp/redef$ cat hello.h
#ifndef TEMPLATE_H
#define TEMPLATE_H
#include <iostream>
template <class T>
class Hello
{
public:
void print_hello(T var);
};
template <class T>
void Hello<T>::print_hello(T var)
{
std::cout << "Hello generic function " << var << "\n";
}
template <> //inline
void Hello<int>::print_hello(int var)
{
std::cout << "Hello specialized function " << var << "\n";
}
#endif
paulo@aeris:~/teste/cpp/redef$ cat other.h
#include <iostream>
void other_func();
paulo@aeris:~/teste/cpp/redef$ cat other.c
#include "other.h"
#include "hello.h"
void other_func()
{
Hello<char> hc;
Hello<int> hi;
hc.print_hello('a');
hi.print_hello(1);
}
paulo@aeris:~/teste/cpp/redef$ cat main.c
#include "hello.h"
#include "other.h"
int main()
{
Hello<char> hc;
Hello<int> hi;
hc.print_hello('a');
hi.print_hello(1);
other_func();
return 0;
}
paulo@aeris:~/teste/cpp/redef$ cat Makefile
all:
g++ -c other.c -o other.o -Wall -Wextra
g++ main.c other.o -o main -Wall -Wextra
Finalmente:
paulo@aeris:~/teste/cpp/redef$ make
g++ -c other.c -o other.o -Wall -Wextra
g++ main.c other.o -o main -Wall -Wextra
other.o: In function `Hello<int>::print_hello(int)':
other.c:(.text+0x0): multiple definition of `Hello<int>::print_hello(int)'
/tmp/cc0dZS9l.o:main.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status
make: ** [all] Erro 1
Se eu descomentar o "inline" dentro de hello.h, o código será compilado e executado, mas isso parece apenas uma espécie de "solução alternativa" para mim: e se a função especializada for grande e usada muitas vezes? Vou pegar um grande binário? Existe alguma outra maneira de fazer isso? Se sim, como? Se não, por quê?
Tentei procurar respostas, mas tudo que consegui foi "usar inline" sem qualquer explicação adicional.
obrigado
Respostas:
Intuitivamente, quando você especializa totalmente algo, ele não depende mais de um parâmetro de modelo - então, a menos que você faça a especialização embutida, você precisa colocá-la em um arquivo .cpp em vez de .h ou você acabará violando o uma regra de definição, como diz David. Observe que quando você especializa parcialmente os modelos, as especializações parciais ainda dependem de um ou mais parâmetros do modelo, portanto, eles ainda vão para um arquivo .h.
fonte
hello.h
.template <typename T>
então, ela poderá entrar em um cabeçalho e, se for,template<>
não será?A palavra
inline
- chave tem mais a ver com dizer ao compilador que o símbolo estará presente em mais de um arquivo de objeto sem violar a Regra de Uma Definição do que com o embutimento real, que o compilador pode decidir fazer ou não fazer.O problema que você está vendo é que sem o inline, a função será compilada em todas as unidades de tradução que incluam o cabeçalho, violando o ODR. Adicionar
inline
aí é o caminho certo a seguir. Caso contrário, você pode encaminhar a declaração de especialização e fornecê-la em uma única unidade de tradução, como faria com qualquer outra função.fonte
Você instanciou explicitamente um modelo em seu cabeçalho (
void Hello<T>::print_hello(T var)
). Isso criará várias definições. Você pode resolver isso de duas maneiras:1) Faça sua instanciação embutida.
2) Declare a instanciação em um cabeçalho e a implemente em um cpp.
fonte
Aqui está uma parte do padrão C ++ 11 relacionada a esse problema:
Portanto, se você fizer algumas especializações explícitas (também conhecidas como completas) de modelos no
*.h
arquivo, ainda precisaráinline
ajudá-lo a se livrar da violação do ODR .fonte