definizione multipla della specializzazione del modello quando si utilizzano oggetti diversi


95

Quando utilizzo un modello specializzato in diversi file oggetto, ricevo un errore di "definizione multipla" durante il collegamento. L'unica soluzione che ho trovato consiste nell'usare la funzione "inline", ma sembra solo una soluzione alternativa. Come risolverlo senza utilizzare la parola chiave "inline"? Se non è possibile, perché?

Ecco il codice di esempio:

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

Finalmente:

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

Se rimuovo il commento "inline" all'interno di hello.h, il codice verrà compilato ed eseguito, ma mi sembra solo una sorta di "soluzione alternativa": cosa succede se la funzione specializzata è grande e utilizzata molte volte? Otterrò un binario grande? C'è un altro modo di fare questo? Se sì, come? In caso contrario, perché?

Ho provato a cercare risposte, ma tutto ciò che ho ottenuto è stato "utilizzare in linea" senza ulteriori spiegazioni.

Grazie


6
inserire l'effettiva implementazione specializzata in .cpp anziché nel file di intestazione
Anycorn

Risposte:


129

Intuitivamente, quando si specializza completamente qualcosa, non dipende più da un parametro del modello, quindi a meno che non si effettui la specializzazione in linea, è necessario inserirla in un file .cpp invece di .h o si finisce per violare il una regola di definizione come dice David. Si noti che quando si specializzano parzialmente i modelli, le specializzazioni parziali dipendono ancora da uno o più parametri del modello, quindi vanno ancora in un file .h.


Hmmm sono ancora un po 'confuso su come infrange l'ODR. Perché definisci il modello completamente specializzato solo una volta. Potresti creare l'oggetto più volte in diversi file oggetto (ad es. In questo caso è istanziato in other.c e main.c) ma l'oggetto originale stesso è definito solo in un file, in questo caso hello.h.
Justin Liang

3
@JustinLiang: L'intestazione è inclusa in due file .c separati - che ha lo stesso effetto come se avessi scritto il suo contenuto (inclusa la specializzazione completa) direttamente nei file in cui è incluso nelle posizioni pertinenti. The One Definition Rule (vedi en.wikipedia.org/wiki/One_Definition_Rule ) dice (tra le altre cose): "In tutto il programma, un oggetto o una funzione non inline non può avere più di una definizione". In questo caso, la specializzazione completa del modello di funzione è in sostanza proprio come una normale funzione, quindi a meno che non sia in linea non può avere più di una definizione.
Stuart Golodetz

Hmmm, ho notato che quando non abbiamo una specializzazione basata su modelli questo errore non verrà visualizzato. Supponiamo di avere due diverse funzioni che sono state definite nel file di intestazione, al di fuori della classe, funzioneranno ancora senza l'inline? Ad esempio: pastebin.com/raw.php?i=bRaiNC7M . Ho seguito quel corso e l'ho incluso in due file. Questo non avrebbe "lo stesso effetto di se avessi scritto il contenuto" direttamente nei due file e quindi ci sarà un errore di definizione multipla?
Justin Liang

@ Justin Liang, il codice dell'intestazione basato sulla classe violerà comunque l'ODR se incluso in più file, a meno che le definizioni delle funzioni non siano all'interno del corpo della classe.
haripkannan

Quindi, se la mia definizione di membro statico è preceduta da template <typename T>, potrebbe essere inserita in un'intestazione, e in tal caso template<>potrebbe non esserlo?
Violet Giraffe

49

La parola chiave inline è più per dire al compilatore che il simbolo sarà presente in più di un file oggetto senza violare la regola di una definizione che per l'inlining effettivo, che il compilatore può decidere di fare o non fare.

Il problema che stai riscontrando è che senza l'inline, la funzione verrà compilata in tutte le unità di traduzione che includono l'intestazione, violando l'ODR. Aggiungere inlinec'è la strada giusta da percorrere. Altrimenti, puoi inoltrare la dichiarazione della specializzazione e fornirla in un'unica unità di traduzione, come faresti con qualsiasi altra funzione.


22

Hai esplicitamente istanziato un modello nell'intestazione (void Hello<T>::print_hello(T var) ). Questo creerà più definizioni. Puoi risolverlo in due modi:

1) Rendi la tua istanziazione in linea.

2) Dichiarare l'istanza in un header e poi implementarla in un cpp.


In realtà c'è un terzo modo che è metterli in uno spazio dei nomi senza nome ... che è simile ad avere statico in C.
Alexis Wilke

4
Non è valido qui. Una specializzazione del modello deve trovarsi nello stesso spazio dei nomi del modello originale.
Edward Strange

0

Ecco alcune parti dello standard C ++ 11 relative a questo problema:

Una specializzazione esplicita di un modello di funzione è inline solo se è dichiarato con l'identificatore inline o definito come cancellato e indipendentemente dal fatto che il modello di funzione sia inline. [ Esempio:

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

template <> inline void f <> (int) {/ * ... /} // OK: inline template <> int g <> (int) {/ ... * /} // OK: non inline - end esempio ]

Quindi, se effettui alcune specializzazioni esplicite (ovvero complete) dei modelli in *.hfile, dovrai comunque inlineaiutarti a sbarazzarti della violazione di ODR .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.