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.
Related
compiler explorer link
Consider the following:
// Variant 1
template<auto> struct require_constexpr;
template<typename R>
constexpr auto is_constexpr_size(R&& r) {
return requires { typename require_constexpr<std::ranges::size(std::forward<R>(r))>; };
}
static_assert(!is_constexpr_size(std::vector{1,2,3,4}));
static_assert(is_constexpr_size(std::array{1,2,3,4}));
The goal here is not the is_constexpr_size function as such, but to find an (requires) expression determining that the size of a range's type is a compile-time constant, so that it can be used in a function taking any range by forwarding-reference in order to if constexpr switch based on it.
Unfortunately this doesn't work since r is of reference type and not usable in a constant expression, although for std::array the call to std::range::sizes will never access the referenced object.
Variant 2: Replacing R&& with R in the function parameter changes this. The constant expression requirements for non-reference type variables are weaker and both MSVC and GCC accept the code with this change, but Clang still doesn't. My understanding is that there is currently a proposal to change the rules, so that the variant with R&& will also work as expected.
However, until this is implemented, I am looking for an alternative, not requiring restriction of the parameter to non-reference types. I also don't want to depend on the range's type being e.g. default-constructible. Therefore I cannot construct a temporary object of the correct type. std::declval is also not usable, since std::ranges::size needs to be evaluated.
I tried the following:
// Variant 3
return requires (std::remove_reference_t<R> s) { typename require_constexpr<std::ranges::size(std::forward<R>(s))>; };
This is accepted by MSVC, but not Clang or GCC. Reading the standard, I am not sure whether this use of a requires parameter is supposed to be allowed.
My questions are as follows:
Regarding std::ranges::size specifically: It takes its argument by forwarding-reference and forwards to some other function. Shouldn't std::ranges::size(r) never be a constant expression (with r a local variable outside the constant expression) for the same reason as in variant 1? If the answer is that it isn't, then assume for the following that std::ranges::size is replaced by a custom implementation not relying on references.
Is my understanding that variant 2 should work correct?
Is variant 3 supposed to work?
If variant 3 is not correct, what is the best way to achieve my goal?
Clarification: That the references are forwarding and that I use std::forward shouldn't be relevant to the question. Maybe I shouldn't have put them there. It is only relevant that the function takes a reference as parameter.
The use case is something like this:
auto transform(auto&& range, auto&& f) {
// Transforms range by applying f to each element
// Returns a `std::array` if `std::range::size(range)` is a constant expression.
// Returns a `std::vector` otherwise.
}
In this application the function would take a forwarding reference, but the check for compile-time constantness shouldn't depend on it. (If it does for some reason I am fine with not supporting such types.)
It is also not relevant to my question that is_constexpr_size is marked constexpr and used in a constant expression. I did so only for the examples to be testable at compile-time. In practice is_constexpr_size/transform would generally not be used in a constant expression, but even with a runtime argument transform should be able to switch return types based on the type of the argument.
If you look closely at the specification of ranges::size in [range.prim.size], except when the type of R is the primitive array type, ranges::size obtains the size of r by calling the size() member function or passing it into a free function.
And since the parameter type of transform() function is reference, ranges::size(r) cannot be used as a constant expression in the function body, this means we can only get the size of r through the type of R, not the object of R.
However, there are not many standard range types that contain size information, such as primitive arrays, std::array, std::span, and some simple range adaptors. So we can define a function to detect whether R is of these types, and extract the size from its type in a corresponding way.
#include <ranges>
#include <array>
#include <span>
template<class>
inline constexpr bool is_std_array = false;
template<class T, std::size_t N>
inline constexpr bool is_std_array<std::array<T, N>> = true;
template<class>
inline constexpr bool is_std_span = false;
template<class T, std::size_t N>
inline constexpr bool is_std_span<std::span<T, N>> = true;
template<auto>
struct require_constant;
template<class R>
constexpr auto get_constexpr_size() {
if constexpr (std::is_bounded_array_v<R>)
return std::extent_v<R>;
else if constexpr (is_std_array<R>)
return std::tuple_size_v<R>;
else if constexpr (is_std_span<R>)
return R::extent;
else if constexpr (std::ranges::sized_range<R> &&
requires { typename require_constant<R::size()>; })
return R::size();
else
return std::dynamic_extent;
}
For the custom range type, I think we can only get its size in a constant expression by determining whether it has a static size() function, which is what the last conditional branch did. It is worth noting that it also applies to ranges::empty_view and ranges::single_view which already have static size() functions.
Once this size detection function is completed, we can use it in the transform() function to try to get the size value in a constant expression, and choose whether to use std::array or std::vector as the return value according to whether the return value is std::dynamic_extent.
template<std::ranges::input_range R, std::copy_constructible F>
constexpr auto transform(R&& r, F f) {
using value_type = std::remove_cvref_t<
std::indirect_result_t<F&, std::ranges::iterator_t<R>>>;
using DR = std::remove_cvref_t<R>;
constexpr auto size = get_constexpr_size<DR>();
if constexpr (size != std::dynamic_extent) {
std::array<value_type, size> arr;
std::ranges::transform(r, arr.begin(), std::move(f));
return arr;
} else {
std::vector<value_type> v;
if constexpr (requires { std::ranges::size(r); })
v.reserve(std::ranges::size(r));
std::ranges::transform(r, std::back_inserter(v), std::move(f));
return v;
}
}
Demo.
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;
}
#cyberpunk_ is trying to achieve something and made some questions about it but all the chase boils down to this:
Is it possible to build a tool to enforce compile-time evaluation of a constexpr function?
int f(int i) {return i;}
constexpr int g(int i) {return i;}
int main()
{
f(at_compilation(g, 0));
int x = at_compilation(g, 1);
constexpr int y = at_compilation(g, 2);
}
In all situations, at_compilation enforce compilation-time evaluation of g.
at_compilation doesn't need to be in this form.
Requirements
Allow any (numerical native) literal type as input for the constexpr function.
this could also be hardcoded based on the function arguments types.
Allow any (numerical native) literal type as output, which is the result of the constexpr function call.
this could also be hardcoded based on the function return type.
Desirables
Reduced macro usage but don't be afraid of using.
Be general (not type hardcoded).
Support any literal type. At last any numerical native literal type is a requirement.
Related Questions:
When does a constexpr function get evaluated at compile time?
Forcing a constant expression to be evaluated during compile-time?
Passing any function as a template parameter?
Where in the C++11 standard does it specify when a constexpr function can be evaluated during translation?
Answers with relevant code samples:
1
2
3 (this one has an illustrative AT_COMPILATION macro)
All the code samples have limitations regarding the requirements.
A clear explanation for how this is unfeasible in C++ is also a good answer.
I suspect it's impossible based on #K-ballo / #Herb Sutter answer which states "and the result is used in a constant expression as well". This was not part of my former conception about constexpr functions, I firstly thought that just passing literals (or other compile-time input) as arguments would suffice to guarantee (by standard) it to be evaluated at compilation-time.
It's already assumed constexpr function's purpose is that they can fit in constant expression situations when necessary, like in array bounds. That's OK. Given that, this question is about a hack on using them just as a tool for compile time calculation. Whether it's a good or bad thing to do should not matter.
I believe that it's impossible because the compiler is only required to compute values that are used at compile-time, and there is no generic expression that can use every part of a value of class type. Computations that initialize private members might even be impossible to force, as you would depend on a public constexpr member function to use the result.
If you could access the object representation by
static_cast< char const * >( static_cast< void const * >( & const_value ) )
then it would be possible to checksum the result of the computation (and use the result as an integral constant expression), forcing the compiler to perform every calculation that isn't moot. But the cast from void * to char * is disallowed in a constant-expression, and likewise attempting to accomplish the same with a union. Even if it were allowed, if the constructor left one byte uninitialized, using an uninitialized value is also forbidden in a constant-expression.
So, even if C++ had better tools for introspection, it would still be impossible to recover the work performed by a constexpr function in order to artificially use some members but not others.
Just to be clear (even if it repeats the question), there's no reason to want this. The language already requires a check that everything can be computed at compile time, if needed, and the only effect of forcing the compiler to non-lazily compute pure values would be to make it slower and use more memory.
Edit (question was radically altered)
If you have several functions returning scalar type, and want to ensure that some of them work as constant expressions under certain arguments, then write test cases using static_assert.
constexpr int g(int i) {return i;}
int i = 5;
static_assert( g( 3 ) == 0, "failure 1" );
static_assert( g( i ) == 5, "failure 2" );
If you don't want to fix the result values, then discard them. (Unfortunately, GCC may optimize out the non-constant part of such an expression, so you might need to do something more baroque on that platform.
static_assert( g( i ) == 5 || true, "failure only if not constexpr" );
As for encapsulating this into a macro, the other linked questions seem to address a lot. If you want to expand one of those answers or to fix a particular bug, it would be better to explain the bug rather than ask us to read so much literature and start from scratch.
Thanks to C++17 (lambda constexpr, auto template parameter, inline as valid template non-type value) we now have a solution:
//implementation
#include <utility>
template<auto X>
using constant = std::integral_constant<decltype(X), X>;
template<class T>
constexpr auto to_constant(T f) //should use && but clang has a bug that would make +f fail
{
constexpr auto ptr = +f; //uses conversion operator to function pointer
return constant<ptr>{}; //not yet implemented for gcc ("no linkage"), working with clang
}
#define constexpr_arg(...) to_constant([]{ return __VA_ARGS__; })
//userland
template<auto Func>
constexpr void func(constant<Func>)
{
constexpr decltype(auto) x = Func();
static_assert(x == 3.14);
}
int main()
{
func(constexpr_arg(3.14));
}
proof it's working : https://godbolt.org/g/vWbyjE
Also this version doesn't work for all cases (mainly if the argument of the macro uses non-constexpr values but still produce a constexpr result).
For such uses cases : https://godbolt.org/g/DRZ5JM
For a gcc version (so portable for now):
//implementation
template<class T>
struct constant
{
static constexpr decltype(auto) value = T::getPtr()();
};
template<class T>
constexpr auto to_constant(T&& f) //remove the && if you want to be also compatible with clang
{
constexpr auto ptr = +f; //uses conversion operator to function pointer
struct A
{
static constexpr auto getPtr() { return ptr; }
};
return constant<A>{};
}
#define constexpr_arg(...) to_constant([]{ return __VA_ARGS__; })
//userland
template<class Constant>
constexpr void func(Constant&&)
{
static_assert(Constant::value == 3.14);
}
int main()
{
func(constexpr_arg(3.14));
}
https://godbolt.org/g/LBCYfi
Use std::integral_constant:
int x = std::integral_constant<int, g(0)>::value;
f(std::integral_constant<int, g(1)>::value);
This code will not compile if g(n) is not evaluated at compile-time.
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.
I tried to find a solution for the problem of the question C++ template non-type parameter type deduction, which does not involve a template parameter to call f, but implicitly chooses the correct type for the template parameter.
Since constexpr should guarantee that a function only contains compile time constants, and is evaluated at compile time (at least thats what i think it does), i thought it might be the solution for this issue.
So i came up with this:
template <class T, T VALUE> void f() {}
//first i tried this:
template <class T> auto get_f(T t) -> decltype( &f<T,t> ) { return f<T,t>; }
//second try:
template <class T> constexpr void (&get_f( T t ))() { return f<T,t>; }
int main()
{
get_f(10)(); //gets correct f and calls it
}
first version generates following error:
error: use of parameter 't' outside function body
which is really confusing, since the usage of parameters in the decltype statement of a trailing return type should be ok?
second version generates following error:
error: invalid initialization of non-const reference of type 'void (&)()'
from an rvalue of type '<unresolved overloaded function type>'
which is kinda confusing, since i fully qualified f in get_f.
I would expect this kind of error messages if i did not have the constexpr. So do i have a false understanding of what constexpr does, or is the C++0x implementation of GCC flawed for this case ?
I am using GCC 4.6.2
Since constexpr should guarantee that a function only contains compile
time constants, and is evaluated at compile time (at least thats what
i think it does), i thought it might be the solution for this issue.
A constexpr function can be used in a constant expression context, but is not restricted to one. In this respect they are different from a metafunction and a regular function. Consider the problem of returning the successor of an integer:
// Regular function
int f(int i)
{ return i + 1; }
// Regular metafunction
template<int I>
struct g {
static constexpr auto value = I + 1;
};
// constexpr function
constexpr int h(int i)
{ return i + 1; }
// Then...
{
// runtime context: the metafunction can't be used
int i;
std::cin >> i;
f(i); // Okay
g<i>::value; // Invalid
h(i); // Okay
// compile time context: the regular function can't be used
char a[f(42)]; // Invalid
char b[g<42>::value]; // Okay
char c[h(42)]; // Okay
}
constexpr has other usages (e.g. constructors) but when it comes to constexpr functions this is the gist of it: some functions should be available in both runtime and constant contexts because some computations are available in both. It's possible to compute i + 1 whether i is a compile-time constant or is extracted from std::cin.
This means that inside the body of a constexpr function the parameters are not themselves constant expressions. So what you are attempting is not possible. Your function can't deal with
int i;
std::cin >> i;
get_f(i); // what's the return type?
and the violation happens here:
constexpr auto get_f(T t)
-> decltype( &f<T,t> ) // <-
Since t is not a constant expression according to the rules of the language (no matter what, even if you actually only pass constant expressions in), it can't appear as the second template parameter of f.
(And in the larger picture it means that no, you can't use argument deduction from function templates to conveniently pass a non-type parameter to a class template.)