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.
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 read about constexpr functions and when they do a compile time calculation. Now i am at a point, where i have to fill an array with new values, thus the array cannot be const.
From what i know, a constexpr function is definitely evaluated at compile time, when every value inside the function is const and used functions are constexpr which also only use const values. Furthermore, an other post stated, that it's possible to force a compile time compilation by making the returned obj const.
Lets say, my function is a constexpr, but has a non-const array inside, but the returned object will be const. Will my function calculation be evaluated at compile time(forced?).
template<int Size, typename T>
struct Array{
T array[Size];
Array(const T * a){
for(int i = 0; i < Size; i++){
array[i] = a[i];
}
}
};
template<typename T, int size>
class Example{
private:
Array<size, T> _array;
public:
constexpr explicit Example(T * arr):_array(arr){};
constexpr explicit Example(const T * arr):_array(arr){};
};
template<typename D, int size, typename ...buf, typename T>
constexpr auto calculations(const T & myObj){
D test1[2];
// calculation fills arr
return Example<D, size>(test1);
}
int main(){
const int size = 2;
const double test1[size] = {1,2};
const auto obj1 = Example<double, size>(test1); //compile time
//obj2 calculations during compile time or run-time?
const auto obj2 = calculations<double, size>(obj1);
}
From what i know, a constexpr function is definitely evaluated at compile time, when every value inside the function is const and used functions are constexpr which also only use const values. Furthermore, an other post stated, that it's possible to force a compile time compilation by making the returned obj const.
All of these statements are wrong. See below.
No, a call to a constexpr function is only guaranteed to be evaluated at compile-time if it is called in a context requiring a (compile-time) constant expression. The initializer of obj2 is not such a context, even if it is const.
You can force the initializer to be compile-time evaluated by declaring obj2 as constexpr. (Which however has very different meaning than const!)
Even then it is not going to work, because calculations<double, size>(obj1) is not actually a constant expression. obj1 is not a compile-time constant without declaring it constexpr as well. Similarly this doesn't work because test1 is not a constant expression without declaring it constexpr as well.
Then you also need to make the constructor of Array constexpr and you need to actually fill the values of test1 inside calculations, because accessing uninitialized values causes undefined behavior and undefined behavior makes expressions not constant expressions.
So all in all:
template<int Size, typename T>
struct Array{
T array[Size];
constexpr Array(const T * a) {
for(int i = 0; i < Size; i++){
array[i] = a[i];
}
}
};
template<typename T, int size>
class Example{
private:
Array<size, T> _array;
public:
constexpr explicit Example(T * arr):_array(arr){};
constexpr explicit Example(const T * arr):_array(arr){};
};
template<typename D, int size, typename ...buf, typename T>
constexpr auto calculations(const T & myObj){
D test1[2];
test1[0] = 0;
test1[1] = 1;
// calculation fills arr
return Example<D, size>(test1);
}
int main(){
const int size = 2;
constexpr double test1[size] = {1,2};
constexpr auto obj1 = Example<double, size>(test1); //compile time
//obj2 calculations during compile time or run-time?
constexpr auto obj2 = calculations<double, size>(obj1);
}
In C++20 there will be an alternative keyword consteval which one can use instead of constexpr on a function to force it to always be evaluated at compile-time. Currently there is no way to do that without making e.g. the destination of the return value a constexpr variable.
In fact your original code has undefined behavior. Because Array does not have a constexpr constructor, objects of that type can never be constructed in constant expressions. And because Example uses that type, it cannot be used in constant expressions either. This makes it illegal to put constexpr on its constructor, because a function declared constexpr causes undefined behavior if there isn't at least one valid set of template arguments and function arguments that would produce a constant expression. (The same then applies to calculations as well, because it uses Example.
So you must put constexpr on the constructor of Array in any case if your program is supposed to be well-formed.
Whether variables created inside the constant expression (e.g. inside calculations) are const does not matter at all.
from Microsoft:
A constexpr function is one whose return value can be computed at
compile time when consuming code requires it. Consuming code requires
the return value at compile time, for example, to initialize a
constexpr variable or provide a non-type template argument. When its
arguments are constexpr values, a constexpr function produces a
compile-time constant. When called with non-constexpr arguments, or
when its value isn't required at compile time, it produces a value at
run time like a regular function. (This dual behavior saves you from
having to write constexpr and non-constexpr versions of the same
function.)
so your calculate function will evaluated compile time if all parameters are constexpr
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.
This code is supposed to check if a float equals the half of an int at compile time using template meta-programming:
#include
struct A {
constexpr static bool value = true;
};
struct B {
constexpr static bool value = false;
};
template<int i>
struct Meta {
constexpr static int value = i/2;
constexpr static bool func(float n) {
return std::conditional_t<n==value,A,B>::value;
}
};
int main( int argc, const char *argv[] ) {
constexpr bool b = Meta<4>::func(2);
std::cout << b << std::endl;
return 0;
}
But it refuses to compile. The compiler is saying that n is not a constant expression:
test.cpp: In substitution of ‘template<bool _Cond, class _Iftrue, class _Iffalse> using conditional_t = typename std::conditional::type [with bool _Cond = (n == (float)2); _Iftrue = A; _Iffalse = B]’:
test.cpp:15:50: required from ‘static constexpr int Meta<i>::func(float) [with int i = 4]’
test.cpp:21:36: required from here
test.cpp:15:50: error: ‘n’ is not a constant expression
return std::conditional_t<n==value,A,B>::value;
^~~~~
test.cpp:15:50: note: in template argument for type ‘bool’
test.cpp: In function ‘int main(int, const char**)’:
test.cpp:21:36: in constexpr expansion of ‘Meta<4>::func((float)2)’
test.cpp:21:38: error: constexpr call flows off the end of the function
constexpr int b = Meta<4>::func(2);
^
What's the problem here? The value being passed to Meta::func is a literal value. It should be treated like a constant.
What I'm interested in is how can I perform different actions based on a value at compile time. It should be possible because all the input required to calculate the output is available at compile time.
I want to know how can I perform different actions(which might involve types) based on a value at compile time. It should be possible because all the input required to calculate the output is available at compile time.
Problem is that there is one single function generated that needs to be callable with both constant expressions (as your literals are) as well as with variably modified values (results of former calculations, for instance).
The constexpr attribute of the function guarantees that the function is evaluated at compile time, if it is possible because of all of the arguments being constant expressions. If any of them is not, the function serves as ordinary function evaluated at runtime. Even if you never use the function this way, it must still be able to handle the case, and thus function arguments in general (n) can never be used as constexpr and in consequence n == value not either; not matter if used in if constexpr or (after your edit) as a template argument.
Actually, however, the inner if constexpr is obsolete anyway: You feed a constexpr function with compile time constants, so the mentioned guarantee applies and your function will be evaluated at compile time, no matter if you use if constexpr or not, thus your function can be simplified to:
constexpr static bool func(float n)
{
return n == value;
}
Be aware that if this did not come out as expected, you couldn't have assigned the result to a constexpr bool either...
Denoted in the comments to the questions already, but important enough for being hinted to again: Be aware, too, of the rounding problems of floating arithmetics and comparison of such values via exact equality!