I once wrote it (EDIT: see below for limitations and explanations). From https://stackoverflow.com/a/10287598/34509 :
template<typename T>
constexpr typename remove_reference<T>::type makeprval(T && t) {
return t;
}
#define isprvalconstexpr(e) noexcept(makeprval(e))
However there are many kinds of constant expressions. The above answer detects prvalue constant expressions.
Explanation
The noexcept(e)
expression gives false
iff e
contains
- a potentially evaluated call to a function that does not have a non-throwing exception-specification unless the call is a constant expression,
- a potentially evaluated
throw
expression, - a potentially evaluated throwable form of
dynamic_cast
ortypeid
.
Note that the function template makeprval
is not declared noexcept
, so the call needs to be a constant expression for the first bullet not to apply, and this is what we abuse. We need the other bullets to not apply aswell, but thanksfully, both a throw
and a throwable dynamic_cast
or typeid
aren’t allowed in constant expressions aswell, so this is fine.
Limitations
Unfortunately there is a subtle limitation, which may or may not matter for you. The notion of “potentially evaluated” is much more conservative than the limits of what constant expressions apply. So the above noexcept
may give false negatives. It will report that some expressions aren’t prvalue constant expressions, even though they are. Example:
constexpr int a = (0 ? throw "fooled!" : 42);
constexpr bool atest = isprvalconstexpr((0 ? throw "fooled!" : 42));
In the above atest
is false, even though the initialization of a
succeeded. That is because for being a constant expression, it suffices that the “evil” non-constant sub-expressions are “never evaluated”, even though those evil sub-expressions are potentially-evaluated, formally.