I have a few questions! I'm confused with template and constexpr and the difference.
I know templates are instantiated at compile time are they executed at compile time or only in run time? Is there a example where I can use them together to get some benefit?
And what is happening if we have a template with constexpr like in this example.
template <typename T>
constexpr T get_sum(T a, T b)
{
return a+b;
}
int main()
{
constexpr int a = get_sum(2,3); // compile time?
const float b = get_sum(2.2,3.2); // compile time?
float c = get_sum(2.2,3.2); // run time?
}
Your get_sum is a function template. get_sum<int> is a function almost like any other function. Don't get confused by template argument deduction, which does happen at compile time. Without deduction your main is exactly the same as:
constexpr int a=get_sum<int>(2,3);
const float b=get_sum<double>(2.2,3.2);
float c=get_sum<double>(2.2,3.2);
In a nutshell, templates get instantiated by the compiler when needed. Once the compiler synthesized a function, eg get_sum<int> this is a function like other functions and whether the function is constexpr is orthogonal to whether it is the result of instantiating a template.
constexpr on a function tells the compiler that the function can be evaluated at compile time. The compiler must evaluate it at compile time when called in a constexpr context. For example constexpr int a is initialized at compile time. A const float might be initilized already by the compiler. Even a (non-const) float might be optimized away completely by the compiler. There is nothing that prevents a compiler to optimize something as long as the observable behvior of the program is the same (none of your 3 variables is actually used).
Ergo:
int main()
{
constexpr int a=get_sum(2,3); // get_sum<int> must be called at compile time
const float b=get_sum(2.2,3.2); // get_sum<double> is likely to be called at compile time
float c=get_sum(2.2,3.2); // get_sum<double> might be called at compile time or runtime
// or not at all, because the call does not
// contribute to observable behavior
}
TL;DR
Whether a function is an intantiation of a function template and whether the function is constexpr are orthogonal.
For constexpr's there is also static_assert.
constexpr can be used both at compile time and runtime.
(C++20 has consteval, which only allows for compile time evaluation).
#include <cassert>
template<typename type_t>
constexpr auto sum(const type_t& value1, const type_t& value2)
{
return value1 + value2;
}
int main()
{
constexpr auto constexpr_value = sum(1, 2); // <== compile time
static_assert(constexpr_value == 3); // <== compile time validation
// or shorter
static_assert(sum(1, 2) == 3); // <== compile time evaluation
// constexpr's also can compile to runtime versions
auto value = sum(2, 3); // <== runtime evaluation
assert(value == 5);
return 0;
}
Related
case1:
#include <string>
inline constexpr std::string test(std::string s) noexcept
{
return s + "xxx";
}
int main()
{
auto s = test("abc");
}
c++20 with gcc 12.1 is built okay, c++17 with gcc 12.1 or c++20/17 with gcc 11.1 was failed,
constexpr std::string, Is this a new feature, or what does this mean?
case2:
#include <string>
int main()
{
constexpr std::string test{"xxxxxx"};
}
And in this case both failed, what is the difference between these two cases.
There are two different use cases of constexpr here:
constexpr functions
constexpr objects
When you see it on a function such as
inline constexpr std::string test(std::string s) noexcept
{
return s + "xxx";
}
the constexpr is not part of the return type, in the same way that inline is not part of the return type; it is part of the function definition. In this case, constexpr says "this function can possibly be run at compile time".
The second use case you've mentioned is tagging an object definition as constexpr. In this use case, you're telling the compiler that this must be a compile time constant, and currently std::string objects cannot be marked constexpr due to the dynamic memory allocation that it does which it performed at runtime.
One thing you may have seen is that the std::string constructor was marked constexpr in C++20. This does not mean that you can create constexpr instances of std::string. It just means that std::string can be constructed and used within constexpr contexts (like a constexpr function).
You may then ask "if std::string requires runtime allocation, how can it be used within a constexpr function?". The basic answer is that the compiler uses a different allocation strategy to enable it, and does extra magic to make sure no undefined behaviour occurs. You can see more info here.
To give a bit more information about constexpr functions, note that I said it could "possibly" be run at compile time. A function marked constexpr can be run at either compile time or runtime, depending on the arguments it's called with
constexpr int double_val(int x) { return 2 * x; }
int main() {
const int y = double_val(4); // likely ran at compile time
int input = 0;
std::cin >> input;
const int z = double_val(input); // run at runtime
}
If you want to force the function to only run at compile time, that's what C++20's consteval keyword allows you to do.
consteval int double_val(int x) { return 2 * x; }
int main() {
const int y = double_val(4); // fine, ran at compile time
int input = 0;
std::cin >> input;
const int z = double_val(input); // ERROR, could not compile
}
The following might not compile either, for you:
constexpr auto s = test("abc");
There's a slight semantical difference between a constexpr object and a constexpr function.
A constexpr object must be a compile-time constant.
A constexpr function might be a compile-time constant if everything in the function is a constant expression. Subsequently, the result of the function is a constant expression if its parameters are, and obviously not if they're not.
What appears to be happening is that your compiler hasn't yet implemented the constexpr version of the std::string constructor in C++20, but has implemented the constexpr + operator.
Hence the function gets compiled without a constant expression as its parameter, but since its result is not assigned to a constexpr object this is not immediately apparent.
But assigning the function's result to a constexpr reveals the ugly truth.
If I want to have a function that behaves the same as a macro, that is, compute the value at compile time, can I use a constexpr function?
For example, can I replace the Foo macro by the foo function and still have a compile time evaluated result in all of the following cases:
#define FOO(x) (x + 2)
constexpr int foo(int x) {
return x + 2;
}
void doSomething(int a) { ... }
int main() {
int res1 = foo(3);
doSomething(foo(4));
const int res2 = foo(5);
return 0;
}
With C++20, consteval might be your friend here:
consteval int foo(int x) {
return x + 2;
}
int main() {
constexpr int r = foo(2);
}
By themselves, constexpr functions are not required to be evaluated at compile time. However, you can force them to be evaluated by assigning the return value to a constexpr variable:
doSomething(foo(4)); // foo(4) not guaranteed to be evaluated at compile time
constexpr auto result = foo(4); // foo(4) _is_ guaranteed to be evaluated at compile time
doSomething(result):
Also, a note about your question on macros. A macro definition has nothing to do with compile time evaluation. It's more akin to an always inline function.
It's impossible to detect if something was evaluated at runtime or compile-time using only the C++ itself, so the as-if rule allows the compiler to do whatever it wants.
Nothing stops a compiler from performing the addition in your macro at runtime, and nothing stops it from calculating even a non-constexpr function at compile-time (as long as it doesn't perform any IO, etc). It all depends on optimization settings and the sanity of the compiler.
Normally, in unoptimized builds, constexpr functions are executed at runtime unless the return value is used in a context that requires a compile-time constant. This includes initializing a constexpr variable from it, and your const int res2 implicitly becomes constexpr because its initializer is constexpr, so foo(5) should be called at compile-time.
In optimized builds, you can expect the compiler to do as much as possible at compile-time. (A function doesn't even have constexpr, as long as the function body is visible in the current translation unit, or if link-time optimizations are enabled.)
I was investigating some rather weird code-coverage results of constexpr functions (compile-time calls can't get instrumented by the code-coverage tool I use), and noticed that some constexpr functions were getting called as runtime functions, if the results of the function call were not stored.
It seems that, for constexpr functions or methods, if you store the results of the call (either in a runtime variable [emphasis!!!] or a constexpr variable), the call is a compile-time call (as long as the parameters are compile-time). If you ignore the results, the call is a runtime call. This has nothing to do with my code-coverage tool; the behavior seems to be repeatable in the simple example below.
You could argue that since constexpr functions cannot have side-effects, it doesn't matter what the compiler does if you don't return / use a result. I'd think that for efficiency, the compiler would still make whatever it can be constexpr, but that's neither here nor there... What I'm wondering is if this is even defined behavior.
Is this a portable way to guarantee your constexpr functions will be invoked as runtime??? There aren't a ton of uses for that, but one use is: if you want to "take credit for tests that were called on constexpr functions" in code coverage, by just calling the same function at the end of your unit test, and ignoring the result, so that they get instrumented.
There are other ways to force a function to get called as runtime, I know, I'm mostly curious about what's going on here. It was very unexpected when I first saw it. Unless this is portable, I'll probably just call my constexpr methods through a runtime object (which seems to do the trick even for static methods), or through a runtime function pointer.
See example below. Live demo: https://onlinegdb.com/rJao0RNGP
// Modified from https://stackoverflow.com/a/40410624/12854372
extern bool no_symbol;
struct ContextIsConstexpr {
size_t s;
constexpr ContextIsConstexpr() : s(1) {}
constexpr void operator()() {
auto ignore = s ? 1 : no_symbol;
}
};
constexpr bool tryIgnoringResult()
{
ContextIsConstexpr()();
return true;
}
constexpr void thereIsNoResult() {
ContextIsConstexpr()();
}
int main()
{
constexpr auto result1 = tryIgnoringResult(); // OK, compile-time
auto result2 = tryIgnoringResult(); // OK, compile-time
// tryIgnoringResult(); // Unexpected, runtime!
// thereIsNoResult(); // Unexpected, runtime!
}
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
Recently I modify some if constexpr into if in my constexpr functions and found they still work fine and can be evaluated when compile time. Here is a minimum case:
template<int N>
constexpr bool is_negative()
{
if constexpr (N >= 0) return false;
else return true;
}
int main()
{
constexpr bool v = is_negative<1>();
}
live demo
In the case above, N must be known at compile time because it is non-type template parameter, so if constexpr works fine here. However, it is a constexpr function, so, iirc, it is possible to get a return value even though I replace if constexpr with if:
template<int N>
constexpr bool is_negative()
{
if (N >= 0) return false;
else return true;
}
int main()
{
constexpr bool v = is_negative<1>();
}
live demo
From cppref, all of the requirements in A constexpr function must satisfy the following requirements: don't mention if. So, IIUC, it should be implementation defined behavior whether constexpr function contain if to be evaluted at compile time even though all related variable is known at compile time(like is_negative above).
So, my conclusion is:
Before c++17, we don't have if constexpr, so the choice is if, which means it is not guaranteed to get our constexpr functions get evaluted at compile time, all depend on compiler implementation
After c++17, if constexpr is preferred if we want constexpr functions get evaluated at compile time.
All above is my personal thoughts, maybe something important missed/misunderstand, feel free to correct me. The question is still unchanged: if and if constexpr, which should be prefered for constexpr functions which expected to be evaluated at compile time.
references:
- What is Allowed in a constexpr Function?
- Difference between "if constexpr()" Vs "if()"
Before c++17, we don't have if constexpr, so the choice is if, which means it is not guaranteed to get our constexpr functions get evaluted at compile time, all depend on compiler implementation
The fact that an if statement is not constexpr does not mean it can't be evaluated at compile time, as part of a constexpr expression. In your example, v is evaluated at compile time in both cases, because it is required to be: it's a constant expression. That's not implementation defined.
After c++17, if constexpr is prefered if we want constexpr functions get evaluated at compile time.
Constexpr if statements were introduced to solve a problem. Getting constexpr functions to get evaluated at compile time is not that problem.
Here is an example where a constexpr if is required instead of a simple if (taken from cppreference):
template <typename T>
auto get_value(T t) {
if constexpr(std::is_pointer_v<T>)
return *t; // deduces return type to int for T = int*
else
return t; // deduces return type to int for T = int
}
Try removing the constexpr keyword and see what happens (demo).
Also, note that you can always solve that problem using other methods, but if constexpr has the advantage of conciseness. For instance, here's an equivalent get_value using tag dispatching:
template<typename T>
auto get_value_impl(T t, std::true_type) {
return *t;
}
template<typename T>
auto get_value_impl(T t, std::false_type) {
return t;
}
template<typename T>
auto get_value(T t) {
return get_value_impl(t, std::is_pointer<T>{});
}
Demo
The difference between if constexpr and if is whether the expression can always be executed at compile time. In your example, you are using a template argument, so it doesn't really matter which one you write. The difference can be noticed if you would have the following code:
constexpr bool is_negative(int n)
{
if (n >= 0) return false;
else return true;
}
int main(int argc, char**)
{
constexpr bool v = is_negative(1);
bool b = is_negative(argc);
return static_cast<int>(v || b);
}
For the code above, writing if constexpr will not work. As you can call the function with a runtime value.
Again, it shouldn't matter as both code paths are valid. Normal compiler optimizations should kick in when using the function with constant values.
The real interest of if constexpr is when only one path is valid:
template <typename T>
constexpr auto get_value(T t) {
if constexpr(std::is_pointer_v<T>)
return *t; // deduces return type to int for T = int*
else
return t; // deduces return type to int for T = int
}
If T would be an int, the code path with *t is invalid as you can't dereference an int. However, because one uses if constexpr instead of if, the code in the false path only needs to be syntactically correct if it depends on the template argument.
As you are searching for a guideline, the compiler already requires: Use if constexpr when one of the code paths is invalid. Use if when depending on arguments.
For the case where the if-condition is calculatable at compile time with 2 valid paths, use if constexpr to require it to be optimized even in debug builds, use if if you want to step through it in debug builds.
If you go to extremes, the expressione could become too complex for the compiler to optimize it in a production build, in which case if constexpr could again be interesting while in the hot path. Personally, I haven't been in this situation yet, however I haven't used it that much.
Am I right to think that this function should only be evaluated at compile time, or is there a run-time cost to it?
template <typename T>
size_t constexpr CompID() {
return typeid(T).hash_code();
}
struct Foo {};
int main(int argc, const char * argv[]) {
size_t foo = CompID<Foo>();
return 0;
}
constexpr function allows the function to be evaluated at compile time, but does not require that, so your answer is "maybe". It depends on the compiler's optimization settings.
§7.1.5[dcl.constexpr]/7
A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function
in all respects except that a call to a constexpr function can appear in a constant expression.
If you wish to have no runtime cost, you could force compile-time evaluation by assigning it to a constexpr variable, e.g.
constexpr auto foo = CompID<Foo>();
Also note that type_info.hash_code() cannot be evaluated in compile-time (it is not a constexpr function, §18.7.1[type.info]/7). So your code is actually wrong.