Is it possible to produce a compile-time boolean value based on whether or not a C++11 expression is a constant expression (i.e. constexpr
) in C++11? A few questions on SO relate to this, but I don't see a straight answer anywhere.

- 10,488
- 6
- 50
- 83
-
4gcc has `__builtin_constant_p()`, http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Other-Builtins.html – Joseph Quinsey Nov 08 '12 at 23:10
-
@JosephQuinsey: Interesting. I am looking for a C++ solution valid for all conformant compilers though: I've edited the question to say this now; thanks. – user2023370 Nov 08 '12 at 23:13
-
Are you interested in "the expression is evaluated during compilation" really just if it has a `constexpr` specifier? – pmr Nov 08 '12 at 23:14
-
@pmr: Yes. That's all. Can it be done? – user2023370 Nov 08 '12 at 23:16
-
1@user643722 Sorry, my comment was missing "or". There are two cases: `true` if `f` has a `constexpr`, false otherwise specifier AND `true` if `f` has a `constexpr` and `fe(x)` is actually `const`. Which do you want the weaker or the stronger condition? – pmr Nov 08 '12 at 23:23
-
1"I.e." means literally "that is." Translate it as "which is to say." Did you mean "e.g."? – Jive Dadson Nov 08 '12 at 23:35
-
@pmr: What are `f`, `fe` and `x` here? – user2023370 Nov 08 '12 at 23:41
-
1@JiveDadson: No, I do mean i.e. – user2023370 Nov 08 '12 at 23:42
-
1@user643722 So you want specifically to know if the value is declared with the keyword constexpr? That is what "i.e." implies, but I do not think most people would consider "a constant expression" and "constexpr" to be synonymous. – Jive Dadson Nov 08 '12 at 23:50
-
@JiveDadson: Not a value, an expression. `1+2` is not declared with the keyword `constexpr`. – user2023370 Nov 08 '12 at 23:59
-
1If it is, it's not going to be done through a template like `is_constexpr
::value`, because if `expr` *isn't* a constant expression, that won't compile. – GManNickG Nov 09 '12 at 01:56 -
`constexpr` does not mean "constant expression". – R. Martinho Fernandes Nov 09 '12 at 16:10
-
1[Exploring `constexpr` at runtime](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3583.pdf) talks about this somewhat. The current status doesn't look like it'll happen any time soon though. – Flexo Mar 01 '15 at 05:36
-
It looks like a standard solution is in the pipeline: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0595r1.html. – user2023370 Jul 02 '18 at 08:26
-
1As of C++20, we should be able to use [`std::is_constant_evaluated()`](https://en.cppreference.com/w/cpp/types/is_constant_evaluated) for this. I'm not yet familiar enough with its usage and potential quirks to write an answer based on this, though, nor would said answer be canonically applicable until next year (barring delays). – Justin Time - Reinstate Monica Jul 18 '19 at 16:54
-
Additional issue is how to answer for: `constexpr int f(int n) { return n == 42 ? throw 42 : n;}` `is_constexpr(f(std::declval
()))`. un-evaluated expression is problematic. – Jarod42 Aug 19 '20 at 07:41
5 Answers
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.

- 3,826
- 5
- 37
- 51

- 496,577
- 130
- 894
- 1,212
-
-
1@sergey i dont understand. can you explain why my method does not work? – Johannes Schaub - litb Nov 09 '12 at 09:24
-
-
1@JohannesSchaub-litb: I'm interested in why your solution works with GCC, but fails with Clang. For example, unlike GCC, Clang reckons integer literals, or `constexpr` integer variables, are not "prvalue" constant expressions (according to your test). Which compilers did you try? – user2023370 Nov 09 '12 at 09:48
-
1
-
1@user thanks for your report. i will try to figure out why it fails on clang later today. – Johannes Schaub - litb Nov 09 '12 at 09:50
-
I see half of it. A constant expression by definition can't throw exceptions. But does the other direction also hold? If it doesn't throw exceptions, then it must be a constant expression? Why? What if T's copy/move constructors are declared noexcept? – glaebhoerl Jan 08 '13 at 17:00
-
@illissius edited the answer to provide an explanation and I also found false negatives. – Johannes Schaub - litb Jan 08 '13 at 18:47
-
3@litb This doesn't work on Clang yet because Clang doesn't check whether a call is a constant expression when deciding whether it is `noexcept`. – Richard Smith May 20 '13 at 06:16
As of 2017, is_constexpr
is not possible in C++11. That sounds like an odd thing to say, so let me explain a bit of the history.
First, we added this feature to resolve a defect: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1129
Johannes Schaub - litb posted a constexpr detection macro that relied on the provision that constant expressions are implicitly noexcept. This worked in C++11, but was never implemented by at least some compilers (for instance, clang). Then, as part of C++17, we evaluated Removing Deprecated Exception Specifications from C++17. As a side-effect of that wording, we accidentally removed that provision. When the Core Working Group discussed adding the provision back in, they realized that there were some serious problems with doing so. You can see the full details in the LLVM bug report. So rather than adding it back in, we decided to consider it a defect against all versions of standard and retroactively removed it.
The effect of this is that there is, to my knowledge, no way to detect whether an expression is usable as a constant expression.

- 26,872
- 14
- 68
- 84
-
-
@David Stone That's a pity, but can't fault your answer. Max kudos for taking the time. – user2023370 Nov 28 '17 at 19:48
-
1I am currently working on a proposal that would allow implementing `is_constexpr` as a macro (must be a macro to avoid side-effects). Hopefully it will be discussed at the next committee meeting. – David Stone Nov 30 '17 at 00:32
-
@DavidStone is your misdirection of the defect link to a XKCD comic intentional? – Charles L Wilcox Dec 13 '17 at 15:35
-
2@CharlesLWilcox: At the risk of killing the joke by explaining it, we were considering adding something in to the next standard, and on further review, removed it from old standards instead. See: the second panel of that comic. – David Stone Dec 14 '17 at 00:43
-
"...we decided to consider it a defect against all versions of [the] standard and retroactively removed it." Citation needed. No joke: I was trying to convince the compiler team earlier this week that implementing the `noexcept(constant_expression)` behavior in C++14 mode is a mistake. If CWG in fact decided to apply this mistake as a DR, it would be convenient. ;) – Casey Feb 02 '18 at 03:50
-
@Casey: Richard Smith says in the clang bug report I linked, "As a result of that, I'm also going to consider this change as an effective DR against C++11 and C++14... but I'm open to reconsidering if we see many user complaints.". Rethinking this answer, should follow up with him to ensure that this is Project Editor Richard Smith talking, not just Clang Contributor Richard Smith. Presumably there is some more official wording somewhere than an LLVM bug report. – David Stone Feb 04 '18 at 03:34
-
@DavidStone Yes, I read the comment in the clang bug a month or so ago: "I consider it to be a DR" is very conspicuously not the same statement as "CWG plans to apply this as a DR." – Casey Feb 08 '18 at 15:10
-
That comment was me with my Clang Developer hat on, not with my Project Editor hat on. I don't believe CWG (or WG21 as a whole) has any official stance on this change being a DR at this time. With my CWG Member hat on, I intend to argue for this being treated as a DR if/when it comes up. – Richard Smith May 02 '18 at 23:24
-
Has anything changed as of 2019, and is there a proposal to make this possible with c++20? – lightxbulb Jul 03 '19 at 11:42
-
1I have [a proposal that would make this possible](https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md). It is targeted at C++23. I will post as an answer if it gets accepted. (Note that some of the formatting is messed up on GitHub) – David Stone Jul 04 '19 at 02:36
Yes, this is possible. One way to do it (which is valid even with the recent noexcept
changes) is to take advantage of the C++11 narrowing conversion rules:
A narrowing conversion is an implicit conversion [...] from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression whose value after integral promotions will fit into the target type.
(emphasis mine). List initialization generally disallows narrowing conversions, and when combined with SFINAE we can build gadgets for detecting whether an arbitrary expression is a constant expression:
// p() here could be anything
template<int (*p)()> std::true_type is_constexpr_impl(decltype(int{(p(), 0U)}));
template<int (*p)()> std::false_type is_constexpr_impl(...);
template<int (*p)()> using is_constexpr = decltype(is_constexpr_impl<p>(0));
constexpr int f() { return 0; }
int g() { return 0; }
static_assert(is_constexpr<f>());
static_assert(!is_constexpr<g>());
The key here is that int{(expr, 0U)}
contains a narrowing conversion from unsigned int
to int
(and thus is ill-formed), unless expr
is a constant expression, in which case the entire expression (expr, 0U)
is a constant expression whose evaluated value fits into the type int
.

- 13,696
- 56
- 78
-
The second assertion fails on MSVC 2017 for some reason or another (you can check with the latest version of goldbolt which at the time of me writing this is 19.21). – lightxbulb Jul 03 '19 at 11:22
-
Similar approach for detecting whether a *static storage* variable is `constexpr`: https://stackoverflow.com/questions/8936549/constexpr-overloading/60714976#60714976 – Amir Kirsh Mar 17 '20 at 01:09
The following is an implementation of is_constexpr
for functions, not for arbitrary expressions, for C++11 and C++17. It requires the arguments to the function you want to test to be default constructible, though.
#include <type_traits>
struct A {}; // don't make it too easy, use a UDT
A f1(A a) { return a; } // is_constexpr -> false
constexpr A f2(A a) { return a; } // is_constexpr -> true
// The following turns anything (in our case a value of A) into an int.
// This is necessary because non-type template arguments must be integral
// (likely to change with C++20).
template <class T> constexpr int make_int(T &&) { return 0; }
// Helper to turn some function type (e.g. int(float)) into a function
// pointer type (e.g. int (*)(float)).
template <class T> struct signature_from;
template <class R, class... Args> struct signature_from<R(Args...)> {
using type = R(*)(Args...);
};
// See std::void_t for the idea. This does it for ints instead of types.
template <int...> using void_from_int = void;
// The fallback case: F is not a function pointer to a constexpr function
template <class T, typename signature_from<T>::type F, class = void_from_int<>>
struct is_constexpr {
static constexpr bool value = false;
};
// If void_from_int<make_int(F(Args()...))> doesn't lead to a substitution
// failure, then this is the preferred specialization. In that case F must
// be a function pointer to a constexpr function. If it is not, it could
// not be used in a template argument.
template <class R, class... Args, typename signature_from<R(Args...)>::type F>
struct is_constexpr<R(Args...), F, void_from_int<make_int(F(Args()...))>>
{
static constexpr bool value = true;
};
// proof that it works:
static_assert(!is_constexpr<A(A), f1>::value, "");
static_assert( is_constexpr<A(A), f2>::value, "");
#if __cplusplus >= 201703
// with C++17 the type of the function can be deduced:
template<auto F> struct is_constexpr2 : is_constexpr<std::remove_pointer_t<decltype(F)>, F> {};
static_assert(!is_constexpr2<f1>::value, "");
static_assert( is_constexpr2<f2>::value, "");
#endif
See it in action at https://godbolt.org/g/rdeQme.

- 449
- 5
- 5
-
Your code fails to compile on MSVC (you can check with the latest version of goldbolt which at the time of me writing this is 19.21). – lightxbulb Jul 03 '19 at 11:26
-
1
C++20 added std::is_constant_evaluated()
This allows checking if a certain expression is a constant evaluated expression, i.e. being evaluated at compile time.
Usage example:
constexpr int foo(int num) {
// below is true in case the condition is being evaluated at compile time
// side note, using: if constexpr (std::is_constant_evaluated())
// would be evaluated always to true, so you should use a simple if!
if (std::is_constant_evaluated()) {
return foo_compiletime(num);
}
else {
return foo_runtime(num);
}
}
int main() {
constexpr auto t1 = foo(6); // reaches foo_compiletime
const auto t2 = foo(6); // reaches foo_compiletime
int n = rand() % 10;
const auto t3 = foo(n); // reaches foo_runtime
auto t4 = foo(6); // unfortunately, reaches foo_runtime
}
The last call in the example above would reach foo_runtime, since the call is not within a constant expression context (the result is not being used as a constant expression, see also this SO answer).
This may lead to undesired pessimization, compared to the case of leaving the decision to the user, who may call:
auto t4 = foo_compiletime(6);
And the compiler is allowed to perform the operations inside foo_compiletime at compile time, if it is declared as constexpr
function, or would be obliged to do that if it is declared consteval
. However, once we leave the decision to the compiler, we will reach foo_runtime, unless we explicitly direct the compiler to go for foo_compiletime, by taking the result into a const
, constexpr
or constinit
variable. Which then, in a way, omits the value of having one function for both scenarios, if the user is required to help the compiler peek the right path.
Another possible option for the call to be optimized, is:
constexpr auto temp = foo(6); // foo_compiletime
auto t4 = temp;
But again, we require the user to be aware of the inner behavior of foo, which is not exactly what we want to achieve.
See the pessimization in this code.
See more on that in this great blog post on the subject.

- 12,564
- 41
- 74