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

@toth gcc не отклоняет его, пока вы его не используете в любом месте, поэтому в этом отношении он немного дружелюбен. Но не имеет смысла просто объявлять переменную, которую вы никогда не используете, поэтому она правильно отвергает ее там, где это важно. – Barry

+0

, но gcc позволяет мне использовать его, если он не находится в static_assert. Например, https://godbolt.org/g/rfbH5c (в то время как clang продолжает отклонять код). Итак, один из компиляторов должен быть не прав, любая идея, которая? – toth