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