2009-08-08 2 views
8

Come alcuni dei miei codice richiesto conversione implicita tra matrici di tipo diverso (ad esempio Matrix<int> a Matrix<double>), che definisce un costruttore di copia template Matrix<T>::Matrix(Matrix<U> const&) invece dello standard Matrix<T>::Matrix(Matrix<T> const&):Templated costruttore di copia non riesce con specifico tipo template

template <typename T> class Matrix { 
public: 
    // ... 
    template <typename U> Matrix(Matrix<U> const&); 
    // ... 
private 
    unsigned int m_rows, m_cols; 
    T *m_data; 
    // ... 
}; 

Con un typecast appropriato aggiunto al copy-constructor, questo metodo viene convertito in modo perfetto tra matrici di tipi diversi. Sorprendentemente, fallisce con un errore malloc proprio nella situazione in cui funzionerebbe un semplice costruttore di copie: dove U == T. Abbastanza sicuro, sovraccaricare il costruttore di copie con la firma predefinita Matrix<T>::Matrix(Matrix<T> const&) risolve il problema.

Questa è una soluzione scadente, in quanto risulta nella duplicazione all'ingrosso del codice copy-constructor (Letteralmente un copia-incolla invariato). Ancora più importante, non capisco perché c'è un errore malloc doppio senza il codice duplicato. Inoltre, perché la sintassi estremamente verbosa template <typename T> template <typename U> qui richiesta rispetto allo standard e molto più succinta, template <typename T, typename U>?

Sorgente completa del metodo basato su modello, compilata utilizzando G ++ v4.0.1 su Mac OS 10.5.

template <typename T> template <typename U> Matrix<T>::Matrix(Matrix<U> const& obj) { 
    m_rows = obj.GetNumRows(); 
    m_cols = obj.GetNumCols(); 
    m_data = new T[m_rows * m_cols]; 

    for (unsigned int r = 0; r < m_rows; ++r) { 
     for (unsigned int c = 0; c < m_cols; ++c) { 
      m_data[m_rows * r + c] = static_cast<T>(obj(r, c)); 
     } 
    } 
} 

risposta

13

Non riesce perché un modello non sopprime la dichiarazione implicita di un costruttore di copie. Servirà come un semplice costruttore di conversione, che può essere usato per copiare un oggetto quando la risoluzione di sovraccarico lo seleziona.

Ora, probabilmente hai copiato la tua matrice da qualche parte, che userebbe il costruttore di copie implicitamente definito che esegue una copia flat. Quindi, la matrice copiata e la copia avrebbero entrambi nel proprio distruttore cancellare lo stesso puntatore.

Inoltre, il motivo per cui è estremamente dettagliato template <typename T> template <typename U> sintassi richiesta

Perché ci sono due modelli coinvolti: The Matrix, che è un modello di classe, e il modello costruttore di conversione. Ogni modello merita la propria clausola template con i propri parametri.

Si dovrebbe sbarazzarsi del <T> nella prima riga, a proposito. Una cosa del genere non appare quando si definisce un modello.

Questa è una soluzione povera, come risulta nella duplicazione all'ingrosso del codice di costruttore di copia

È possibile definire un modello di funzione di membro, che farà il lavoro, e delegato sia dal costruttore di conversione e costruttore di copie. In questo modo, il codice non è duplicato.


Richard ha fatto un buon punto nei commenti che mi ha fatto modificare la mia risposta. Se la funzione candidata generata dal modello è una corrispondenza migliore rispetto al costruttore di copie dichiarato implicitamente, il modello "vince" e verrà chiamato.Qui ci sono due esempi comuni:

struct A { 
    template<typename T> 
    A(T&) { std::cout << "A(T&)"; } 
    A() { } 
}; 

int main() { 
    A a; 
    A b(a); // template wins: 
      // A<A>(A&) -- specialization 
      // A(A const&); -- implicit copy constructor 
      // (prefer less qualification) 

    A const a1; 
    A b1(a1); // implicit copy constructor wins: 
      // A(A const&) -- specialization 
      // A(A const&) -- implicit copy constructor 
      // (prefer non-template) 
} 

un costruttore di copia può avere un parametro di riferimento non-const troppo, se uno dei suoi membri ha

struct B { B(B&) { } B() { } }; 
struct A { 
    template<typename T> 
    A(T&) { std::cout << "A(T&)"; } 
    A() { } 
    B b; 
}; 

int main() { 
    A a; 
    A b(a); // implicit copy constructor wins: 
      // A<A>(A&) -- specialization 
      // A(A&); -- implicit copy constructor 
      // (prefer non-template) 

    A const a1; 
    A b1(a1); // template wins: 
      // A(A const&) -- specialization 
      // (implicit copy constructor not viable) 
} 
+0

La tua spiegazione per l'errore malloc suona perfettamente. C'è una ragione specifica per cui i compilatori non possono utilizzare una funzione membro modello come copia-costruttore? Grazie per l'informazione e il consiglio per evitare la duplicazione del codice. –

+0

Se si dispone di quel modello, non è ancora una funzione. Solo l'utilizzo di esso con alcuni argomenti del template genera una funzione (membro-) al di fuori di essa (chiamata specializzazione). Questo è anche il motivo per cui i template dei membri non possono essere virtuali: non si sa in anticipo quali funzioni sono generate da esso. –

+0

Questo ha molto senso - questo non funziona proprio perché è necessario inoltrare i parametri del modello o includere l'intera fonte dell'implementazione. Grazie ancora. –

1

Io non sono del tutto chiaro dalla tua domanda, ma Sospetto che ciò che sta accadendo sia che il costruttore di copie predefinito (che fa solo una copia membro) venga usato in alcune parti del codice. Ricorda, non solo il codice che scrivi effettivamente usa il costruttore di copie, anche il compilatore lo usa.

Problemi correlati