2016-04-26 33 views
6

考慮下面的代碼:編譯器錯誤使用CRTP與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; 

這編譯海合會正常,但如果我取消了static_assert線,就抱怨說,

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

我用不同的試了一下gcc版本從4.9到5.3,我得到了同樣的錯誤(你可以在godbolt here上試試)。鐺拒絕編譯它即使沒有static_assert,並抱怨說

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

哪個編譯器是正確的(如果有的話)? 有沒有一種很好的方法來修復代碼?

回答

9

訪問嵌套名稱需要類是完整的,但Derived是不完整的現在還沒有到:

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

所以代碼是非法的形成。

有幾種解決方法。首先,你可以分別只是在值傳遞用作模板參數:

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

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

其次,如果可能的話(例如,你不需要x_derived聲明任何成員),您可以將值移動到一個功能延遲其實例:

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

感謝您的偉大答案巴里。所以,如果我正確地理解了你的話,gcc接受沒有static_assert的代碼是一個編譯器錯誤,而且clang在拒絕它的時候是正確的? – toth

+1

@tcc gcc不會拒絕它,除非你真的在任何地方使用它 - 所以它只是在這方面有點友善。但是聲明一個你永遠不會使用的變量並不是真正有意義的 - 所以它正確地拒絕它在重要的地方。 – Barry

+0

但gcc確實讓我使用它,只要它不在static_assert中。例如https://godbolt.org/g/rfbH5c(同時clang繼續拒絕該代碼)。所以其中一個編譯器一定是錯的,有什麼想法呢? – toth