2016-04-26 32 views
6

Si consideri il seguente codice:Compiler errore quando utilizza CRTP con static_assert

template<typename Derived> 
struct Base { 
    static constexpr int x_base = Derived::x_derived; 
    //static_assert(x_base > 1, "Oops"); 
}; 

struct Derived : public Base<Derived> { 
    static constexpr int x_derived = 5 ; 
}; 

Base<Derived> obj; 

Questo compila bene su gcc ma se togliere il commento alla linea di static_assert, si lamenta che

error: incomplete type 'Derived' used in nested name specifier 
static constexpr int x_base = Derived::x_derived; 

ho provato con diversi versioni di gcc da 4.9 a 5.3 e ottengo lo stesso errore (puoi provarlo su Godbolt here). clang si rifiuta di compilarlo anche senza la static_assert, e si lamenta che

error: no member named 'x_derived' in 'Derived' 
static constexpr int x_base = Derived::x_derived; 

quale compilatore è corretto (se del caso)? C'è un bel modo per correggere il codice?

risposta

9

Accesso nomi nidificati richiede la classe per essere completa, ma Derived non è completa ancora arrivato:

template<typename Derived> 
struct Base { 
    static constexpr int x_base = Derived::x_derived; 
            ^^^^^^^^^ 
}; 

modo che il codice è mal formato.

Esistono alcuni accorgimenti. In primo luogo, è possibile separatamente solo passare il valore come argomento di un template:

template <typename Derived, int x_derived> 
struct Base { 
    static constexpr int x_base = x_derived; 
}; 

struct Derived : public Base<Derived, 5> { }; 

In secondo luogo, se possibile (ad esempio, non è necessario x_derived dichiarare tutti i membri), è possibile spostare il valore in una funzione di ritardare la sua istanziazione:

template<typename Derived> 
struct Base { 
    static constexpr int x_base() { 
     static_assert(Derived::x_derived > 1, "Oops"); 
     return Derived::x_derived; 
    } 

}; 

struct Derived : public Base<Derived> { 
    static constexpr int x_derived = 5; 
}; 
+0

Grazie per l'ottima risposta Barry. Quindi, se ho capito bene, il fatto che gcc accetti il ​​codice senza static_assert è un bug del compilatore, e clang è corretto nel respingerlo? – toth

+1

@toth gcc non lo rifiuta finché non lo utilizzi in qualsiasi luogo, quindi è solo un po 'più amichevole a tale riguardo. Ma non è davvero significativo dichiarare semplicemente una variabile che non usi mai, quindi la rifiuta correttamente dove è importante. – Barry

+0

ma gcc mi consente di usarlo, purché non sia in static_assert. Ad esempio, https://godbolt.org/g/rfbH5c (mentre clang continua a rifiutare il codice). Quindi uno dei compilatori deve essere sbagliato, qualche idea che? – toth