багаторазове визначення спеціалізації шаблону при використанні різних об'єктів


95

Коли я використовую спеціалізований шаблон у різних об’єктних файлах, під час зв’язування з’являється помилка «множинного визначення». Єдине рішення, яке я знайшов, полягає у використанні функції "вбудована", але це, здається, якесь обхідне рішення. Як це вирішити, не використовуючи ключове слово "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

Якщо я прокоментую "вбудований" всередині hello.h, код буде скомпільований і запущений, але це просто здається мені якимсь "обхідним шляхом": що робити, якщо спеціалізована функція велика і використовується багато разів? Чи отримаю великий двійковий файл? Чи є інший спосіб зробити це? Якщо так, то як? Якщо ні, то чому?

Я намагався шукати відповіді, але все, що я отримав, було "використовувати вбудований" без будь-яких додаткових пояснень.

Дякую


6
помістіть фактичну спеціалізовану реалізацію в .cpp, а не у файл заголовка
Anycorn

Відповіді:


129

Інтуїтивно зрозуміло, що коли ви повністю спеціалізуєтесь на чомусь, це вже не залежить від параметра шаблону - тому, якщо ви не зробите спеціалізацію вбудованою, вам потрібно помістити її у файл .cpp замість .h або в кінцевому підсумку порушити одне правило визначення, як каже Девід. Зверніть увагу, що коли ви частково спеціалізуєтесь на шаблонах, часткова спеціалізація все одно залежить від одного або кількох параметрів шаблону, тому вони все ще переходять у файл .h.


Хммм, я все ще трохи розгублений щодо того, як це порушує ОДР. Оскільки ви визначаєте повністю спеціалізований шаблон лише один раз. Можливо, ви створюєте об'єкт кілька разів у різних файлах об'єктів (тобто, у цьому випадку він створюється в інших файлах .c та main.c), але сам оригінальний об'єкт визначається лише в одному файлі - у цьому випадку hello.h.
Джастін Лян

3
@JustinLiang: Заголовок включений у два окремі файли .c - це має такий самий ефект, як якщо б ви написали його вміст (включаючи повну спеціалізацію) безпосередньо у файли, до яких він включений у відповідних місцях. Правило єдиного визначення (див. En.wikipedia.org/wiki/One_Definition_Rule ) говорить (серед іншого): "У всій програмі об'єкт або невбудована функція не може мати більше одного визначення". У цьому випадку повна спеціалізація шаблону функції по суті подібна до звичайної функції, тому, якщо вона не є вбудованою, вона не може мати більше одного визначення.
Стюарт Голодец,

Хм-м-м-м, я помітив, що коли у нас немає спеціальної спеціалізації, ця помилка не з'явиться. Скажімо, у нас було дві різні функції, які були визначені у файлі заголовка, поза класом, вони все одно працюватимуть без вбудованого? Наприклад: pastebin.com/raw.php?i=bRaiNC7M . Я взяв цей клас і включив його у два файли. Хіба це не матиме "того самого ефекту, як якщо б ви записали вміст" безпосередньо у два файли, і, отже, буде помилка множинного визначення?
Джастін Лян

@Justin Liang, ваш код заголовка на основі класу все одно буде порушувати ODR, якщо включений до декількох файлів, якщо визначення функції не знаходяться всередині тіла класу.
haripkannan

Отже, якщо моєму визначенню статичного члена передує template <typename T>тоді, воно може перейти в заголовок, а якщо це так, template<>то це може і не статися?
Вайолет Жираф

49

Ключове слово inline- це більше про те, щоб сказати компілятору, що символ буде присутній у більш ніж одному об’єктному файлі, не порушуючи правило One Definition, ніж про фактичне вбудовування, яке компілятор може вирішити робити чи не робити.

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


22

Ви явно створили інстанцію шаблону у своєму заголовку ( void Hello<T>::print_hello(T var)). Це створить кілька визначень. Вирішити це можна двома способами:

1) Зробіть примірник вбудованим.

2) Оголосіть екземпляр у заголовку, а потім реалізуйте його в cpp.


Насправді існує третій спосіб, який полягає в тому, щоб помістити їх у простір імен без імені ..., що схоже на наявність статичного в C.
Alexis Wilke

4
Це не діє тут. Спеціалізація шаблону повинна знаходитися в тому самому просторі імен, що і вихідний шаблон.
Edward Strange

0

Ось частина стандарту C ++ 11, пов’язана з цією проблемою:

Явна спеціалізація шаблону функції є вбудованою, лише якщо вона оголошена за допомогою вбудованого специфікатора або визначена як видалена, і незалежно від того, чи є шаблон її функції вбудованим. [Приклад:

template void f (T) {/ * ... /} шаблон вбудований T g (T) {/ ... * /}

template <> inline void f <> (int) {/ * ... /} // OK: inline template <> int g <> (int) {/ ... * /} // OK: not inline - end приклад]

Отже, якщо ви зробите деякі явні (а саме повні) спеціалізації шаблонів у *.hфайлі, то вам все одно доведеться inlineдопомогти вам позбутися порушення ОРС .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.