2016-04-26 52 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