web-dev-qa-db-fra.com

définition multiple de la spécialisation de modèle lors de l'utilisation de différents objets

Lorsque j'utilise un modèle spécialisé dans différents fichiers objets, j'obtiens une erreur de "définition multiple" lors de la liaison. La seule solution que j'ai trouvée consiste à utiliser la fonction "en ligne", mais cela semble être une solution de contournement. Comment résoudre ce problème sans utiliser le mot clé "en ligne"? Si ce n'est pas possible, pourquoi?

Voici l'exemple de code:

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

Finalement:

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 je décommente le "inline" dans hello.h, le code se compilera et s'exécutera, mais cela me semble être une sorte de "solution": que se passe-t-il si la fonction spécialisée est grande et utilisée plusieurs fois? Vais-je obtenir un gros binaire? Y a-t-un autre moyen de faire ça? Si oui, comment? Sinon, pourquoi?

J'ai essayé de chercher des réponses, mais tout ce que j'ai obtenu était "utiliser en ligne" sans autre explication.

Merci

74
pzanoni

Intuitivement, lorsque vous spécialisez complètement quelque chose, cela ne dépend plus d'un paramètre de modèle - donc, sauf si vous faites la spécialisation en ligne, vous devez le placer dans un fichier .cpp au lieu d'un .h ou vous finissez par violer le une règle de définition comme le dit David. Notez que lorsque vous spécialisez partiellement des modèles, les spécialisations partielles dépendent toujours d'un ou de plusieurs paramètres de modèle, donc elles vont toujours dans un fichier .h.

101
Stuart Golodetz

Le mot clé inline consiste davantage à indiquer au compilateur que le symbole sera présent dans plusieurs fichiers objet sans violer la règle de définition unique qu'à l'inliner réel, ce que le compilateur peut décider de faire ou de ne pas faire.

Le problème que vous voyez est que sans l'inline, la fonction sera compilée dans toutes les unités de traduction qui incluent l'en-tête, violant l'ODR. Ajouter inline est la bonne solution. Sinon, vous pouvez transmettre la spécialisation et la fournir dans une seule unité de traduction, comme vous le feriez avec n'importe quelle autre fonction.

Vous avez explicitement instancié un modèle dans votre en-tête (void Hello<T>::print_hello(T var)). Cela créera plusieurs définitions. Vous pouvez le résoudre de deux manières:

1) Faites votre instanciation en ligne.

2) Déclarez l'instanciation dans un en-tête puis implémentez-la dans un cpp.

19
Crazy Eddie