and c++ sfinae typetraits static-assert

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");

Demo en vivo