2010-12-15 12 views
56

Quando si utilizza un modello specializzato in diversi file oggetto, viene visualizzato un errore di "definizione multipla" durante il collegamento. L'unica soluzione che ho trovato riguarda l'uso della funzione "inline", ma sembra solo una soluzione. Come faccio a risolverlo senza usare la parola chiave "in linea"? Se questo non è possibile, perché?definizione multipla della specializzazione del modello quando si utilizzano oggetti diversi

Ecco il codice di esempio:

[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 

Infine:

[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 

Se togliere il commento alla "linea" all'interno ciao.h, il codice verrà compilato ed eseguito, ma che sembra proprio come una sorta di "soluzione" per me: che cosa se la funzione specializzata è grande e usata molte volte? Riceverò un grosso binario? C'è un altro modo di fare questo? Se sì, come? Se no, perché?

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

Grazie

+5

inserire l'implementazione specializzata in .cpp piuttosto che nel file di intestazione – Anycorn

risposta

82

Intuitivamente, quando si specializza completamente qualcosa, non dipende più da un parametro di modello, quindi a meno che non si effettui la specializzazione in linea, è necessario inserirlo in un file .cpp anziché in un .h o finiscono per violare l'unica 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 continuano a essere inseriti in un file .h.

+0

Hmmm Sono ancora un po 'confuso su come si rompe l'ODR. Perché definisci il modello completamente specializzato solo una volta. Potresti creare l'oggetto più volte in diversi file oggetto (ad esempio in questo caso è istanziato in other.c e main.c) ma l'oggetto originale stesso è definito solo in un file - in questo caso 'ciao.h' . –

+3

@JustinLiang: l'intestazione è inclusa in due file .c separati, il che ha lo stesso effetto che se avessi scritto il suo contenuto (compresa la specializzazione completa) direttamente nei file in cui è incluso nelle posizioni pertinenti. La regola One Definition (vedi http://en.wikipedia.org/wiki/One_Definition_Rule) dice (tra le altre cose): "Nell'intero programma, un oggetto o una funzione non in linea non può avere più di una definizione". In questo caso, la piena specializzazione del template di funzione è essenzialmente come una normale funzione, quindi a meno che non sia in linea non può avere più di una definizione. –

+0

Hmmm, ho notato che quando non abbiamo una specializzazione basata su modelli questo errore non verrà visualizzato. Diciamo che avessimo due diverse funzioni che erano definite nel file di intestazione, al di fuori della classe, funzionerebbero ancora senza la linea? Ad esempio: http://pastebin.com/raw.php?i=bRaiNC7M. Ho preso quella lezione e l'ho inclusa in due file. Questo non avrebbe "lo stesso effetto come se avessi scritto il contenuto" direttamente nei due file e quindi ci sarà un errore di definizione multipla? –

30

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

Il problema che si sta verificando è che senza l'inline, la funzione verrà compilata in tutte le unità di traduzione che includono l'intestazione, violando l'ODR. Aggiungendo inline c'è la strada giusta da percorrere. Altrimenti, puoi inoltrare la specializzazione e fornirla in una singola unità di traduzione, come faresti con qualsiasi altra funzione.

15

Hai istanziato esplicitamente 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'intestazione e quindi implementarla in un cpp.

+0

In realtà c'è un terzo modo che consiste nel mettere quelli in uno spazio dei nomi senza nome ... che è simile ad avere statico in C. –

+2

Non è valido qui. Una specializzazione modello deve essere nello stesso spazio dei nomi del modello originale. –

Problemi correlati