How to ensure constexpr function never called at runtime?
In C++20 you can just replace constexpr
by consteval
to enforce a function to be always evaluated at compile time.
Example:
int rt_function(int v){ return v; }
constexpr int rt_ct_function(int v){ return v; }
consteval int ct_function(int v){ return v; }
int main(){
constexpr int ct_value = 1; // compile value
int rt_value = 2; // runtime value
int a = rt_function(ct_value);
int b = rt_ct_function(ct_value);
int c = ct_function(ct_value);
int d = rt_function(rt_value);
int e = rt_ct_function(rt_value);
int f = ct_function(rt_value); // ERROR: runtime value
constexpr int g = rt_function(ct_value); // ERROR: runtime function
constexpr int h = rt_ct_function(ct_value);
constexpr int i = ct_function(ct_value);
}
Pre C++20 workaround
You can enforce the use of it in a constant expression:
#include<utility>
template<typename T, T V>
constexpr auto ct() { return V; }
template<typename T>
constexpr auto func() {
return ct<decltype(std::declval<T>().value()), T{}.value()>();
}
template<typename T>
struct S {
constexpr S() {}
constexpr T value() { return T{}; }
};
template<typename T>
struct U {
U() {}
T value() { return T{}; }
};
int main() {
func<S<int>>();
// won't work
//func<U<int>>();
}
By using the result of the function as a template argument, you got an error if it can't be solved at compile-time.
Constexpr functions not called at compile-time if result is ignored
It might be confusing, but constexpr functions should be called at compile time only in constexpr contexts (assignation to constexpr variable, use for array size or template parameter, ...).
In regular context, functions are called at runtime. Optimizer might resolve that function at compile time (as for any other functions following the as-if rule). constexpr
is indeed a good hint for optimization to happen.
You could argue that since constexpr functions cannot have side-effects
They can have side effect, see following valid example:
constexpr int f(int i)
{
if (i == 0) return 0;
std::cout << i << std::endl;
return i;
}
int main()
{
[[maybe_unused]] constexpr int zero = f(0); // Compile time
f(42); // runtime
}
Demo
constexpr error at compile-time, but no overhead at run-time
Is there a way to cause a compile-time error with a constexpr
function, but not do anything at run time?
You can use the exact same trick, but instead of using a throw-expression, use an expression that is not a constant expression but does what you want at runtime. For instance:
int runtime_fallback(int x) { return x; } // note, not constexpr
constexpr int f(int x) {
return (x != 0) ? x : runtime_fallback(0);
}
constexpr int k1 = f(1); // ok
constexpr int k2 = f(0); // error, can't call 'runtime_fallback' in constant expression
int k3 = f(0); // ok
Do relaxed constexpr
rules in C++1y (C++14) change anything?
Not in this area, no. There are some forms of expression that are valid in constant expressions in C++14 but not in C++11, but neither throw-expressions nor calls to non-constexpr
functions are on that list.
How to force a constexpr function to be evaluated at compile time
You could use non-type template arguments:
template <int N> constexpr int force_compute_at_compile_time();
const int a = force_compute_at_compile_time<omg()>();
Since N
is a template argument, it has to be a constant expression.
constexpr constructor not called as constexpr for implicit type conversion
Does anyone know of a way I can change my code to get the conversion constructor to be called at compile-time if it can be
As I said in linked posted, call of constexpr
functions at compile time is done only in constant expression.
Parameters are not constexpr.
One workaround would be to use MACRO:
#define APPLY_DISPATCHER(dispatcher, str, ...) \
do { \
constexpr callable_type_t<decltype(dispatcher), decltype(make_tuple(__VA_ARGS__))> callable(str); \
(dispatcher)(callable, __VA_ARGS__); \
} while (0)
with
template <typename Dispatcher, typename Tuple> struct callable_type;
template <typename Dispatcher, typename ... Ts>
struct callable_type<Dispatcher, std::tuple<Ts...>>
{
using type = typename Dispatcher::template Callable<Ts...>;
};
template <typename Dispatcher, typename Tuple>
using callable_type_t = typename callable_type<Dispatcher, Tuple>::type;
With usage:
APPLY_DISPATCHER(dispatcher, "one", 1);
APPLY_DISPATCHER(dispatcher, "a", 1); // Fail at compile time as expected
Demo.
But not really better than proposed dispatcher.dispatch(MAKE_CHAR_SEQ("a"), 1);
(or with extension dispatcher.dispatch("a"_cs, 1);
) (providing dispatch overload to be able to create constexpr
Callable
).
Is it possible to ensure a constexpr function is called at most once at compile time?
Short answer: no, because constexpr
functions cannot read/set external state. (They can have internal state, but they still need to be "pure").
Real answer: probably yes, but it's a bad idea. There is a series of blog posts by Filip Roséen which covers the implementation of stateful constexpr
functions by abusing friend
ship and ADL:
"NON-CONSTANT CONSTANT-EXPRESSIONS IN C++" - (cached by Google)
"HOW TO IMPLEMENT A CONSTANT-EXPRESSION COUNTER IN C++" - (cached by Google)
"HOW TO IMPLEMENT A COMPILE-TIME META-CONTAINER IN C++" - (cached by Google)
The technique is very arcane and complicated. It is considered an abuse of features by CWG, which is trying to make it ill-formed with issue #2118.
Related Topics
Advantages of Classes with Only Static Methods in C++
Why Don't Std::Vector's Elements Need a Default Constructor
Comparing 3 Modern C++ Ways to Convert Integral Values to Strings
Fastest Inline-Assembly Spinlock
Std::Forward_List and Std::Forward_List::Push_Back
Looking for 16-Bit X86 Compiler
C++Cli. Are Native Parts Written in Pure C++ But Compiled in Cli as Fast as Pure Native C++
Initialize Global Array of Function Pointers at Either Compile-Time, or Run-Time Before Main()
Compile Openmp Programs with Gcc Compiler on Os X Yosemite
How to Use Const in Vectors to Allow Adding Elements, But Not Modifications to the Already Added
Do Stl Iterators Guarantee Validity After Collection Was Changed
Constructor Initialization VS Assignment
C++ Ifstream Error Using String as Opening File Path
Difference Between Logical and Physical Const-Ness