C++ allows declaring functions as constexpr
. Such functions could be evaluated at compile time and used, for example in if constexpr(...)
. However, a non-constexpr function in this context is not allowed.
So, if we want to write a template, accepting a function and using it in either if constexpr(...)
or old plain if(...)
we need to test whether the function is constexpr, otherwise compile fails as illustrated below:
template<auto F> bool is_zero() { if constexpr(F() == 0) { // error: constexpr if condition is not a constant expression return true; } else { return false; } } int notzero() { return 1; } constexpr int zero() { return 0; } bool test() { auto z1 = is_zero<notzero>(); // in instantiation of function template requested here auto z2 = is_zero<zero>(); return z1 || z2; }
So we need to rewrite our is_zero
as the following ...
template<auto F> bool is_zero() { if constexpr(is_constexpr<F>::value) { if constexpr(F() == 0) { return true; } else return false; } else { return F() == 0; } }
... and implement is_constexpr
using SFINAE:
// is_constexpr.h template<auto F> struct constexpr_true : std::true_type {}; template<auto F> static auto test_constexpr(int) -> constexpr_true<F()>; template<auto F> static auto test_constexpr(long) -> std::false_type; template<auto F> struct is_constexpr : decltype(test_constexpr<F>(0)) {};
is_constexpr template has a constexpr member value, that can be used, for example in static_assert or if constexpr,
as illustarted in the example below
#include "is_constexpr.h" int notzero() { return 1; } constexpr int zero() { return 0; } static_assert(!is_constexpr<notzero>::value); static_assert(is_constexpr<zero>::value); template<auto F> bool is_zero() { if constexpr(is_constexpr<F>::value) { if constexpr(F() == 0) { return true; } else return false; } else { return F() == 0; } } bool test() { return is_zero<zero>() || is_zero<notzero>(); }
Post a Comment