Static_assert, если выражения constexpr
Я хочу создать шаблон класса
template <class T>
class X {
// here I'll use T::value (among other things)
};
T::value
часто является статической переменной constexpr, но не всегда. T::value
должно быть положительным, поэтому я хочу, чтобы люди знали это во время компиляции, когда это возможно.
Если T::value
всегда constexpr, я бы добавил static_assert
как
static_assert(T::value > 0, "need positive number");
Можно ли добавить этот static_assert только в случаях, когда T::value
является constexpr?
Ответы
Ответ 1
Мы можем написать функцию шаблона is_valid
(придумать лучшее имя) с двумя перегрузками:
template <typename T, int N = T::value>
constexpr bool is_valid(int) {
return N > 0;
}
template <typename T>
constexpr bool is_valid(...) {
return true;
}
Первая перегрузка будет действительна только в том случае, если T::value
является константным выражением, иначе это будет SFINAEd. Вторая перегрузка действительна, несмотря ни на что, поэтому мы устраняем перегрузку с помощью фиктивного параметра int
.
Теперь мы проверим его так:
static_assert(is_valid<T>(0), "need positive number");
Live Demo
Ответ 2
Это работает для меня на clang++:
#include <type_traits>
// The default case, returns true if the value is not constant.
template <typename T, typename = void>
struct IsNonConstantOrPositive {
static const bool value = true;
};
// The `constexpr` case. We check if we can evaluate `T::value == 0` which can only
// be evaluated at compile-time if `T::value` is constant. The whole `enable_if` thing
// is to provide a substitution to ensure SFINAE.
template <typename T>
struct IsNonConstantOrPositive<T, typename std::enable_if<T::value==0||true>::type> {
static const bool value = T::value > 0;
};
template <typename T>
struct X {
static_assert(IsNonConstantOrPositive<T>::value, "T::value should be positive");
};
Пример:
struct A { // const > 0, should succeed
static const int value = 123;
};
struct B { // const <= 0, should fail
static const int value = -1234;
};
struct C { // non-const, should succeed
static int value;
};
int main() {
X<A> a; // ok
//X<B> b; // error
X<C> c; // ok
}
Ответ 3
Я думаю, было бы неплохо иметь тест для const (expr) -ness переменной, который будет использоваться как:
struct T {
...
static_assert(!IS_CONSTANT_VAR(value) || value > 0, "trouble is afoot");
};
В приведенной ниже реализации используется аналогичная стратегия для решения kennytm для отказа от неконстантных ссылок. Он работает в Clang и GCC.
#include <type_traits> // enable_if
template<typename T, T& var, typename = void> struct is_constant_var_impl {
static constexpr bool value = false;
};
template<typename T, T& var>
struct is_constant_var_impl <T, var, typename std::enable_if<(double)var == (double)var>::type> {
// (double) cast above to thwart GCC agressive constant folding;
// perhaps could be removed with a bit more finesse
static constexpr bool value = true;
};
#define IS_CONSTANT_VAR(...) (is_constant_var_impl<decltype(__VA_ARGS__), (__VA_ARGS__)>::value)
Pros
- Шаблон можно повторно использовать для разных классов или статических имен участников.
- Самоочевидный код в
static_assert
Против
- Не работает в MSVC
- (Может быть?) Использует С++ 14
- Gross