Когда я использую специализированный шаблон в разных объектных файлах, я получаю ошибку «множественное определение» при связывании. Единственное решение, которое я нашел, включает использование «встроенной» функции, но это похоже на обходной путь. Как мне решить эту проблему без использования ключевого слова "inline"? Если это невозможно, то почему?
Вот пример кода:
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
В заключение:
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
Если я раскомментирую «inline» внутри hello.h, код будет скомпилирован и запущен, но мне это кажется своего рода «обходным путем»: что, если специализированная функция большая и используется много раз? Получу ли я большой двоичный файл? Есть ли другой способ сделать это? Если да, то как? Если нет, то почему?
Я попытался найти ответы, но все, что я получил, это «использовать встроенный» без каких-либо дополнительных объяснений.
Спасибо
Ответы:
Интуитивно понятно, что когда вы что-то полностью специализируете, это больше не зависит от параметра шаблона - поэтому, если вы не сделаете специализацию встроенной, вам нужно поместить ее в файл .cpp вместо .h, иначе вы в конечном итоге нарушите одно правило определения, как говорит Дэвид. Обратите внимание, что когда вы частично специализируете шаблоны, частичные специализации все еще зависят от одного или нескольких параметров шаблона, поэтому они по-прежнему помещаются в файл .h.
источник
hello.h
.template <typename T>
тогда оно может перейти в заголовок, а если оно,template<>
то нет?Ключевое слово
inline
больше сообщает компилятору, что символ будет присутствовать более чем в одном объектном файле без нарушения правила единого определения, чем о фактическом встраивании, которое компилятор может решить делать или не делать.Проблема, которую вы видите, заключается в том, что без встроенного кода функция будет скомпилирована во всех единицах перевода, которые включают заголовок, что нарушает ODR. Добавление
inline
туда - правильный путь. В противном случае вы можете напрямую объявить специализацию и предоставить ее в одной единице перевода, как и с любой другой функцией.источник
Вы явно создали экземпляр шаблона в своем header (
void Hello<T>::print_hello(T var)
). Это создаст несколько определений. Решить ее можно двумя способами:1) Сделайте свой экземпляр встроенным.
2) Объявите экземпляр в заголовке, а затем реализуйте его в cpp.
источник
Вот часть стандарта C ++ 11, связанного с этой проблемой:
Поэтому, если вы сделаете некоторые явные (или полные) специализации шаблонов в
*.h
файле, вам все равно нужноinline
будет помочь вам избавиться от нарушения ODR .источник