At the moment, we have two primary options for compile-time evaluation: template metaprogramming (generally using template structs and/or variables), and constexpr
operations1.
template<int l, int r> struct sum_ { enum { value = l + r }; }; // With struct.
template<int l, int r> const int sum = sum_<l, r>::value; // With struct & var.
template<int l, int r> const int sub = l - r; // With var.
constexpr int mul(int l, int r) { return l * r; } // With constexpr.
Of these, we are guaranteed that all four can be evaluated at compile time.
template<int> struct CompileTimeEvaluable {};
CompileTimeEvaluable<sum_<2, 2>::value> template_struct; // Valid.
CompileTimeEvaluable<sum<2, 2>> template_struct_with_helper_var; // Valid.
CompileTimeEvaluable<sub<2, 2>> template_var; // Valid.
CompileTimeEvaluable<mul(2, 2)> constexpr_func; // Valid.
We can also guarantee that the first three will only be evaluable at compile time, due to the compile-time nature of templates; we cannot, however, provide this same guarantee for constexpr
functions.
int s1 = sum_<1, 2>::value;
//int s2 = sum_<s1, 12>::value; // Error, value of i not known at compile time.
int sv1 = sum<3, 4>;
//int sv2 = sum<s1, 34>; // Error, value of i not known at compile time.
int v1 = sub<5, 6>;
//int v2 = sub<v1, 56>; // Error, value of i not known at compile time.
int c1 = mul(7, 8);
int c2 = mul(c1, 78); // Valid, and executed at run time.
It is possible to use indirection to provide an effective guarantee that a given constexpr
function can only be called at compile time, but this guarantee breaks if the function is accessed directly instead of through the indirection helpers (as noted in the linked answer's comments). It is also possible to poison a constexpr
function such that calling it at runtime becomes impossible, by throw
ing an undefined symbol, thus providing this guarantee by awkward hack. Neither of these seems optimal, however.
Considering this, my question is thus: Including current standards, C++20 drafts, proposals under consideration, experimental features, and anything else of the sort, is there a way to provide this guarantee without resorting to hacks or indirection, using only features and tools built into and/or under consideration for being built into the language itself? [Such as, for example, an attribute such as (both theoretical) [[compile_time_only]]
or [[no_runtime]]
, usage of std::is_constant_evaluated, or a concept, perhaps?]
1: Macros are technically also an option, but... yeah, no.
See Question&Answers more detail:
os