2013-03-22 8 views
6
class A { 
    virtual A* foo() = 0; 
}; 

template<class T> 
class B : public A { 
    virtual T* foo() { return nullptr; } 
}; 

class C : public B<C> { 

}; 

Questa è un'implementazione semplificata per Possibility to mix composite pattern and curiously recurring template pattern. Ottengo il seguente errore:Errore di compilazione CRTP e polimorfismo dinamico

Return type of virtual function 'foo' is not covariant with the return type of the function it overrides ('C *' is not derived from 'A *') 

provata su clang 3.0, GCC 4.7 e Visual Studio 2008.

Prima soluzione:

class C : public A, public B<C> {} 

compila in Visual Studio con un avvertimento che B è già un figlio di A e fa non compilare in clang con errore iniziale.

Un'altra soluzione:

class D : public A {} 
class C : public B<D> {} 

risolve il problema incompletezza, ma io non riesco a capire quante istanze A avrò. L'intuizione mi dice che A è virtuale, quindi dovrebbe essercene solo uno.

Anche questa soluzione alternativa crea codice illeggibile.

Che cosa indica lo standard su questa situazione? Questo codice dovrebbe essere compilato? Se no, perché?

+2

È sempre bene tenere a mente che 'C' è incompleto nella sua lista di basi. –

risposta

4

La funzione virtual A::foo() restituisce un A*, mentre la funzione B<C>::foo(), che si propone di ignorare che, restituisce un C*.

Questo in teoria fa rispettare il principio di covarianza, poiché C è davvero una specializzazione (deriva dal A), ma al punto di istanziazione, questo non è noto, perché C è un tipo incompleto.

Un possibile modo per ripensare il vostro disegno è quello di rendere A un modello di classe pure, e lasciare che B propagare la tesi modello per T fino a A:

template<typename T> 
class A { 
    virtual T* foo() = 0; 
}; 

template<class T> 
class B : public A<T> { 
    virtual T* foo() { return nullptr; } 
}; 

Per quanto riguarda la vostra soluzione:

What does the standard states about this situation? Should this code compile? If not, why?

Non deve essere compilato, poiché il semplice fatto di creare C deriva anche da A in modo esplicito (avviso, si finirebbe con due distinti sotto-oggetti di base di tipo A all'interno di C) non costituiscono un tipo completo C durante l'istanziazione di B<C>. Per paragrafo 9.2/2 della norma C++ 11:

A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.

+0

Sfortunatamente questo rompe l'unico scopo di questo codice: creare la classe di base polimorfa A. –

+0

* funzione B :: foo(), che ha lo scopo di sovrascriverlo, restituisce una B ** - sto leggendo male, o fa 'B :: foo()' restituisce 'C *' invece? – ulidtko

+1

@ulidtko: corretto, quello era un mio errore. Grazie. –