2012-04-29 9 views
8

Quando le funzioni del membro speciale (in particolare, copia/sposta i costruttori e gli operatori di assegnazione copia/sposta) di una classe template sono istanziate? Non appena la classe stessa viene istanziata, o solo quando sono necessari?Quando sono state create istanze di membri speciali di una classe template?

Questo avviene nella seguente situazione:

template <class T, class U> 
struct pair 
{ 
    T first;     
    U second;     

    pair() : first(), second() {} 

    pair(const pair&) = default; 
}; 

struct S 
{ 
    S() {} 
    S(const S&) = delete; 
    S(S&&) = default; 
}; 

int main() 
{ 
    pair<int, S> p; 
} 

Clang si rifiuta di compilare questo codice, con i seguenti errori:

test.cpp:9:5: error: the parameter for this explicitly-defaulted copy constructor is const, but a member or base requires it to be 
     non-const 
    pair(const pair&) = default; 
    ^
test.cpp:21:18: note: in instantiation of template class 'pair<int, S>' requested here 
    pair<int, S> p; 
       ^

suggerendo che esso tenta di creare un'istanza il costruttore di copia non appena la classe è istanziata.

GCC, tuttavia, compila il codice in modo corretto, suggerendo che proverebbe solo a creare un'istanza del costruttore di copie se fosse effettivamente necessario.

Quale comportamento del compilatore è corretto?

(. Una discrepanza simile è esposto per operatori di assegnazione)

UPDATE: Questo ha qualcosa a che fare con il fatto che il costruttore di copia di pair in questo esempio è default Ed, perché se cambio la sua definizione a

pair(const pair& p) : first(p.first), second(p.second) {} 

quindi il codice passa clang pure.

+0

Compila bene con clang3.0 con -std = C++ 11. –

risposta

2

Il passaggio rilevante dello standard è [dcl.fct.def.default]/1:

Una funzione che viene esplicitamente default deve [...] hanno lo stesso tipo di funzione dichiarata (ad eccezione di eventuali differenti qualificativi di ref e salvo che nel caso di un costruttore di copia o di un operatore di assegnazione copia, il tipo di parametro può essere "riferimento a non-const T", dove è il nome della classe della funzione membro) come se fosse era stato implicitamente dichiarato

Questa regola si applica anche se la funzione predefinita non viene mai utilizzata. Ora, [class.copy]/9 dice:

Il costruttore di copia implicitamente dichiarata avrà la forma

X::X(const X&)

se [...] per tutti i membri di dati non statici di X di tipo classe M [...], ogni tipo di classe ha un costruttore di copie il cui primo parametro è di tipo const M& o const volatile M&.

In caso contrario, il costruttore di copia implicitamente dichiarata avrà la forma

X::X(X&)

Pertanto, un esempio di questo tipo è mal formato (e dovrebbe presentare la diagnostica che state vedendo):

struct A { 
    A(); 
    A(A&); // Note, non-const type A in copy constructor 
}; 
template<typename T> 
struct B { 
    T t; 
    B(); 
    B(const B&) = default; 
}; 
B<A> b; // error, B<A>::B(const B&) is defaulted but has the wrong type 

Tuttavia, nel tuo esempio, questa regola non si applica. A causa di un bug di clang (che è già stato risolto), i costruttori di copie cancellati sono stati erroneamente considerati come aventi tipi di parametri non const, che portano a questo errore.

5

Dai un'occhiata alla sezione 14.7.1 dell'attuale standard C++ 11. Per citare dalla versione n3242 del progetto:

l'istanza implicita di una specializzazione classe template provoca l'istanza implicita delle dichiarazioni, ma non dei definizioni o argomenti di default, le funzioni di membro di classe, classi membro, membri di dati statici e modelli membri; e causa l'istanza implicita delle definizioni delle unioni anonime membro . A meno che un modello di classe o un modello membro non sia stato esplicitamente esplicitato o esplicitamente specializzato, la specializzazione del membro viene implicitamente creata quando la specializzazione fa riferimento a un contesto che richiede la definizione del membro ; in particolare, l'inizializzazione (e gli eventuali effetti collaterali associati di ) di un membro di dati statici non si verifica a meno che lo membro di dati statici sia esso stesso utilizzato in un modo che richiede la definizione del membro di dati statici.

Quindi, questo significa che quando si utilizza una classe come un tipo come sopra, solo le dichiarazioni vengono istanziate con esso. Pertanto, l'implementazione effettiva (predefinita) del costruttore di copie non dovrebbe essere istanziata, in quanto non è necessaria nel codice precedente. Quindi GCC lo sta gestendo correttamente, mentre Clang no.

anche la modifica suggerisce, che Clang sta generando l'implementazione per il costruttore di copia di default troppo presto, dal momento che il costruttore di copia implementata direttamente è difettoso così (non è possibile chiamare il costruttore di copia per S come si sta facendo in una propria implementazione).Poiché l'implementazione predefinita e la tua implementazione dovrebbero essere uguali in tutti gli aspetti (incluso il tempo di istanziazione), considererei questo un bug clang.

Problemi correlati