Подтвердить что ты не робот

Множественное определение специализации шаблона при использовании разных объектов

Когда я использую специализированный шаблон в разных объектных файлах, при связывании я получаю ошибку "множественного определения". Единственное решение, которое я нашел, связано с использованием функции "inline" , но это похоже на некоторое обходное решение. Как я могу решить это, не используя ключевое слово "inline" ? Если это невозможно, почему?

Вот пример кода:

[email protected]:~/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

[email protected]:~/teste/cpp/redef$ cat other.h 
#include <iostream>

void other_func();

[email protected]:~/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);
}

[email protected]:~/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;
}

[email protected]:~/teste/cpp/redef$ cat Makefile
all:
    g++ -c other.c -o other.o -Wall -Wextra
    g++ main.c other.o -o main -Wall -Wextra

Наконец:

[email protected]:~/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, код будет компилироваться и запускаться, но это просто похоже на какой-то "обходной путь" для меня: что, если специализированная функция большая и используется много раз? Я получу большой двоичный файл? Есть ли другой способ сделать это? Если да, то как? Если нет, то почему?

Я попытался найти ответы, но все, что у меня было, было "использовать inline" без каких-либо дополнительных объяснений.

Спасибо

4b9b3361

Ответ 1

Интуитивно, когда вы полностью специализируетесь на чем-то, он больше не зависит от параметра шаблона - поэтому, если вы не сделаете специализацию inline, вам нужно поместить ее в файл .cpp вместо .h или вы закончите нарушая одно правило определения, как говорит Дэвид. Обратите внимание: когда вы частично специализируете шаблоны, частичные специализации все еще зависят от одного или нескольких параметров шаблона, поэтому они все равно идут в файл .h.

Ответ 2

Ключевое слово inline больше связано с сообщением компилятору о том, что этот символ будет присутствовать в нескольких объектных файлах, не нарушая правило одного определения, чем о фактической вставке, которую компилятор может решить или не делать.

Проблема, которую вы видите, заключается в том, что без встроенного метода функция будет скомпилирована во всех единицах перевода, которые включают заголовок, нарушая ODR. Добавляя inline, вы можете выбрать правильный путь. В противном случае вы можете переслать объявление специализации и предоставить его в одной единице перевода, как и с любой другой функцией.

Ответ 3

Вы явно создали шаблон в своем заголовке (void Hello<T>::print_hello(T var)). Это создаст несколько определений. Вы можете решить его двумя способами:

1) Создайте свой встроенный экземпляр.

2) Объявите экземпляр в заголовке, а затем реализуйте его в cpp.

Ответ 4

Вот часть стандарта С++ 11, относящаяся к этой проблеме:

Явная специализация шаблона функции является встроенной, только если она объявляется с помощью встроенного спецификатора или определяется как удаленный, и независимо от того, является ли его шаблон функции встроенным. [Пример:

template void f(T) {/*.../} template inline T g(T) {/... */}

template<> inline void f<>(int) {/*.../} //OK: inline template<> int g<>(int) {/... */}//OK: not inline — end example ]

Таким образом, если вы сделаете несколько явных (ака полный) специализаций шаблонов в файле *.h, то вам все равно понадобится inline, чтобы помочь вам избавиться от нарушения ODR.