Skip to content

is_constexpr – a template to test if a function is constexpr

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>();
}

Live is_constexpr example at godbolt.org

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*

*