Kiedy używam specjalistycznego szablonu w różnych plikach obiektowych, podczas łączenia pojawia się błąd „wielokrotnej definicji”. Jedyne rozwiązanie, które znalazłem, polega na użyciu funkcji „inline”, ale wydaje się, że jest to jakieś obejście. Jak rozwiązać ten problem bez użycia słowa kluczowego „inline”? Jeśli to niemożliwe, dlaczego?
Oto przykładowy kod:
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
Wreszcie:
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
Jeśli odkomentuję „inline” wewnątrz hello.h, kod skompiluje się i uruchomi, ale wydaje mi się to pewnym „obejściem”: co jeśli wyspecjalizowana funkcja jest duża i używana wiele razy? Czy dostanę duży plik binarny? Czy jest inny sposób, aby to zrobić? Jeśli tak, w jaki sposób? Jeśli nie, dlaczego?
Próbowałem poszukać odpowiedzi, ale wszystko, co otrzymałem, to „użyj inline” bez dalszych wyjaśnień.
Dzięki
Odpowiedzi:
Intuicyjnie, kiedy w pełni specjalizujesz się w czymś, nie zależy to już od parametru szablonu - więc jeśli nie wprowadzisz specjalizacji w tekście, musisz umieścić ją w pliku .cpp zamiast .h lub w końcu naruszysz jedna zasada definicji, jak mówi David. Zwróć uwagę, że jeśli częściowo specjalizujesz się w szablonach, częściowe specjalizacje nadal zależą od jednego lub większej liczby parametrów szablonu, więc nadal znajdują się w pliku .h.
źródło
hello.h
.template <typename T>
to może trafić do nagłówka, a jeśli tak,template<>
to może nie?Słowo kluczowe
inline
dotyczy bardziej informowania kompilatora, że symbol będzie obecny w więcej niż jednym pliku obiektowym bez naruszania reguły jednej definicji, niż rzeczywistego wstawiania, które kompilator może zdecydować, czy zrobić, lub nie.Problem, który widzisz, polega na tym, że bez inline funkcja zostanie skompilowana we wszystkich jednostkach tłumaczeniowych, które zawierają nagłówek, co narusza ODR. Dodanie
inline
tego jest właściwą drogą. W przeciwnym razie możesz zadeklarować specjalizację i podać ją w pojedynczej jednostce tłumaczeniowej, tak jak w przypadku każdej innej funkcji.źródło
Wyraźnie utworzyłeś wystąpienie szablonu w swoim header (
void Hello<T>::print_hello(T var)
). Spowoduje to utworzenie wielu definicji. Możesz go rozwiązać na dwa sposoby:1) Utwórz instancję w tekście.
2) Zadeklaruj instancję w nagłówku, a następnie zaimplementuj ją w cpp.
źródło
Oto fragment standardu C ++ 11 związany z tym problemem:
Więc jeśli stworzysz jakieś wyraźne (czyli pełne) specjalizacje szablonów w
*.h
pliku, nadal będziesz musiałinline
pomóc Ci pozbyć się naruszenia ODR .źródło