and - constexpr in c++
static_assert si las expresiones son constexpr (3)
Creo que sería bueno tener una prueba para const (expr) -ness de una variable, para ser usada como:
struct T {
...
static_assert(!IS_CONSTANT_VAR(value) || value > 0, "trouble is afoot");
};
La implementación a continuación utiliza una estrategia similar a la solución de kennytm para fallar en referencias no constantes. Funciona en Clang y 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''s 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
- La plantilla se puede reutilizar en diferentes clases o nombres de miembros estáticos
- Código autoexplicativo en el
static_assert
Contras
- No funciona en MSVC
- (¿Quizás?) Usa C ++ 14
- Bruto
Quiero crear una plantilla de clase
template <class T>
class X {
// here I''ll use T::value (among other things)
};
T::value
a menudo será una variable estática constexpr, pero no siempre. T::value
tiene que ser un valor positivo, por lo que quiero que la gente lo sepa durante la compilación, cuando sea posible.
Si T::value
siempre fuera constexpr, agregaría static_assert
like
static_assert(T::value > 0, "need positive number");
¿Es posible agregar esta static_assert solo en los casos en que T::value
es constexpr?
Esto funciona para mí en 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");
};
Ejemplo:
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
}
Podemos escribir una función de plantilla is_valid
( is_valid
un nombre mejor) con dos sobrecargas:
template <typename T, int N = T::value>
constexpr bool is_valid(int) {
return N > 0;
}
template <typename T>
constexpr bool is_valid(...) {
return true;
}
La primera sobrecarga solo será válida si T::value
es una expresión constante, de lo contrario será SFINAEd out. La segunda sobrecarga es válida sin importar qué, así que desambiguamos la sobrecarga con un parámetro int
ficticio.
Ahora lo probamos así:
static_assert(is_valid<T>(0), "need positive number");