I would like the ability to write different functions depending on whether the argument to a function is a compile-time constant or not. What I want is similar to what is outlined here:
http://stackoverflow.com/questions/15232758/detecting-constexpr-with-sfinaeHowever, the solution there only works for determining whether a particular function was declared as constexpr. It cannot be used to determine whether a function argument is a known value at compile time. Similarly, the is_constexpr function outlined here:
http://stackoverflow.com/questions/13299394/is-is-constexpr-possible-in-c11 cannot be used with enable_if and a trailing return type because function arguments are never considered constexpr, even if they are compile-time constants known to the compiler in a constexpr function. That is to say, the following code will not work:
namespace {
template<typename T>
constexpr typename std::remove_reference<T>::type makeprval(T && t) {
return t;
}
#define is_constexpr(e) noexcept(makeprval(e))
template<typename T>
constexpr auto f(T && t) -> typename std::enable_if<is_constexpr(t), bool>::type {
return true;
}
template<typename T>
constexpr auto f(T && t) -> typename std::enable_if<!is_constexpr(t), bool>::type {
return false;
}
} // namespace
int main() {
static_assert(f(0), "Should be constexpr");
int a = 0;
static_assert(!f(a), "Should not be constexpr");
}
Even though the value passed to t in the first case is a compile-time constant expression, it is not usable in a context that requires a constant expression.
I have several use cases for this (and several other people do as well, judging by the number of StackOverflow questions asking if this is possible). The one that is motivating this proposal is a class that represents an integer with compile-time bounds (`ranged_integer`). A ranged_integer in general should only be explicitly constructible from a built-in integer type. However, if it can be statically determined that the value falls within the range of this ranged_integer, then I would like to allow an implicit constructor. Right now, I can only do this if the range of the type of the built-in integer type falls entirely within the range of the ranged_integer. This dramatically reduces the usefulness, as most ranged_integer types will have a range smaller than that of int, but integer literals are by default typed as int.
The practical effect of this is that declaring a constexpr array of ranged_integer is much more cumbersome than it should be. I would like to be able to just declare something like
static constexpr ranged_integer<0, 40> array[] = {
5, 7, 2, 8, 3, 0, 36
};
And get compile-time checks that all of the values are in the range. Instead, the best I can do (and require my users to do!) is something like
using type = ranged_integer<0, 40>;
static constexpr type array[] = {
type(5), type(7), type(2), type(8), type(3), type(0), type(36)
};
My class also provides automatic flexing of ranges in arithmetic operations. For instance, ranged_integer<0, 1> * ranged_integer<1, 2> gives a type of ranged_integer<0, 2>. When doing arithmetic with constant values, my users have to either accept an unnecessarily wide range (all the type system can tell me is that this number you are multiplying by is equivalent to ranged_integer<INT_MIN, INT_MAX>) or else have them again call the constructor or use a factory function. Currently I have used the factory function approach in my own code, so ranged_integer<0, 2>(1) * make_ranged<5>() gives a type of ranged_integer<0, 10> with a value of 5.
If it were possible to declare a function parameter as constexpr, then we could overload on it and all of these problems would go away. The other solution would be to have a library function is_constexpr that works for function parameters and can be used with enable_if. However, this would be tricky when you account for compiler optimizations that lead to parameters being known compile-time constants in a release build due to inlining, but not in debug builds.