2016-04-26 14 views
6

Betrachten Sie den folgenden Code ein:Compilerfehler, wenn CRTP Verwendung mit 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; 

Dies kompiliert auf gcc in Ordnung, aber wenn ich die static_assert Linie Kommentar-, wirft sie

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

Ich versuchte es mit verschiedenen Versionen von gcc von 4.9 bis 5.3 und ich bekomme den gleichen Fehler (Sie können es auf godbolt here versuchen). Klirren weigert sich, es auch ohne die static_assert zu kompilieren, und beschwert sich, dass

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

Which Compiler korrekt ist (falls vorhanden)? Gibt es eine gute Möglichkeit, den Code zu reparieren?

Antwort

9

verschachtelten Namen Zugriff erfordert die Klasse vollständig zu sein, aber Derived ist nicht vollständig hier noch:

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

so wird der Code schlecht ausgebildet.

Es gibt einige Problemumgehungen. Erstens können Sie separat übergeben Sie einfach den Wert als Vorlage Argument:

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

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

Zweitens, wenn möglich (zB Sie nicht x_derived brauchen keine Mitglieder zu erklären), können Sie den Wert in eine Funktion bewegen zu verzögern seine Instanziierung:

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

Danke für die gute Antwort Barry. Also, wenn ich Sie richtig verstanden habe, ist die Tatsache, dass gcc den Code ohne static_assert akzeptiert, ein Compiler-Bug, und clang ist korrekt, wenn er diesen zurückweist? – toth

+1

@toth gcc lehnt es nicht ab, bis du es irgendwo benutzt hast - in dieser Hinsicht ist es nur ein bisschen freundlicher. Aber es ist nicht wirklich sinnvoll, nur eine Variable zu deklarieren, die Sie nie benutzen - also lehnt sie sie dort ab, wo sie wichtig ist. – Barry

+0

aber gcc lässt mich es verwenden, solange es nicht in einer static_assert ist. Zum Beispiel https://godbolt.org/g/rfbH5c (während clang den Code weiterhin ablehnt). Also muss einer der Compiler falsch liegen, irgendeine Idee welche? – toth