Cuando utilizo una plantilla especializada en diferentes archivos de objeto, obtengo un error de "definición múltiple" al vincular. La única solución que encontré implica el uso de la función "en línea", pero parece una solución alternativa. ¿Cómo soluciono eso sin usar la palabra clave "en línea"? Si eso no es posible, ¿por qué?
Aquí está el código de ejemplo:
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
Si elimino el comentario "en línea" dentro de hello.h, el código se compilará y se ejecutará, pero eso me parece una especie de "solución": ¿qué pasa si la función especializada es grande y se usa muchas veces? ¿Obtendré un binario grande? Hay alguna otra manera de hacer esto? Si es así, ¿cómo? Si no, ¿por qué?
Traté de buscar respuestas, pero todo lo que obtuve fue "usar en línea" sin más explicaciones.
Gracias
Respuestas:
Intuitivamente, cuando se especializa completamente en algo, ya no depende de un parámetro de plantilla, por lo que, a menos que realice la especialización en línea, debe colocarla en un archivo .cpp en lugar de .h o terminará violando la una regla de definición como dice David. Tenga en cuenta que cuando se especializa parcialmente en las plantillas, las especializaciones parciales aún dependen de uno o más parámetros de la plantilla, por lo que todavía van en un archivo .h.
fuente
hello.h
.template <typename T>
entonces puede ir a un encabezado, y si estemplate<>
así, ¿puede que no?La palabra clave
inline
tiene más que ver con decirle al compilador que el símbolo estará presente en más de un archivo de objeto sin violar la regla de una definición que con la inserción real, que el compilador puede decidir hacer o no hacer.El problema que está viendo es que sin el inline, la función se compilará en todas las unidades de traducción que incluyen el encabezado, violando el ODR. Agregar
inline
es el camino correcto a seguir. De lo contrario, puede reenviar declarar la especialización y proporcionarla en una sola unidad de traducción, como lo haría con cualquier otra función.fuente
Ha creado una instancia explícitamente de una plantilla en su encabezado (
void Hello<T>::print_hello(T var)
). Esto creará múltiples definiciones. Puedes solucionarlo de dos formas:1) Haga su instanciación en línea.
2) Declare la instanciación en un encabezado y luego impleméntela en un cpp.
fuente
Aquí hay una parte del estándar C ++ 11 relacionada con este problema:
Por lo tanto, si realiza algunas especializaciones explícitas (también conocidas como completas) de plantillas en el
*.h
archivo, aúninline
deberá ayudarlo a deshacerse de la violación de ODR .fuente