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!
Related
Maybe the title is not clear, so concretely:
#include <type_traits>
template<typename T>
constexpr int test(T)
{
return std::is_integral<T>::value;
}
int main()
{
constexpr int a = test(1); // right
constexpr int b = test(1.0); // right
int c = 2;
constexpr int d = test(c); // ERROR!
return 0;
}
In fact, the function doesn't use anything but the type of the parameter, which can be determined obviously in the compilation time. So why is that forbidden and is there any way to make constexpr get the value when only the type of parameter is used?
In fact, I hope to let users call the function through parameters directly rather than code like test<decltype(b)>, which is a feasible but not-convenient-to-use way, to check if the types of parameters obey some rules.
Just take T by reference so it doesn't need to read the value:
template<typename T>
constexpr int test(T&&)
{
return std::is_integral<std::remove_cvref_t<T>>::value;
}
You can even declare test with consteval, if you want to.
(Note that stripping cv-qualifiers isn't necessary in this instance; cv-qualified integral types satisfy the std::is_integral trait.)
Why can't constexpr be used for non-const variables when the function only uses the types?
Because the call expression test(c) is not a constant expression and hence it cannot be used as an initializer for d.
Basically, for the call expression test(c) to be a constant expression, c must be a constant expression. It doesn't matter whether c is used or not inside the function itself. This is why, the call expressions test(1) and test(1.0) works and can be used as an initializer for a and b respectively.
Take a look at the following code example:
template<bool val>
struct test {
static const int value_a = val;
const int value_b = val;
constexpr int get_value_a() const noexcept { return value_a; }
constexpr int get_value_b() const noexcept { return value_b; }
};
int main(int argc, char** argv) {
auto t = test<true>{};
static_assert(t.value_a);
// static_assert(t.value_b);
static_assert(t.get_value_a());
// static_assert(t.get_value_b());
}
Both gcc and clang agree that this should compile, but including any of the commented out static asserts makes it invalid. For example, gcc would then produce these error messages:
error: non-constant condition for static assertion
error: the value of ‘t’ is not usable in a constant expression
note: ‘t’ was not declared ‘constexpr’
This makes perfect sense to me and is exactly what I would have thought. But I don't really know why the other two static asserts compile in the first place. What is the relevant paragraph from the standard that allows this?
In particular, how is this formalized? Is there a clearly defined difference between just using a variable, versus actually accessing its runtime value (which then would be the only forbidden thing in a constexpr context)?
It's just the rules. Before constexpr, const variables initialised with constant expressions could be used as constant expressions themselves (Also for some compatibility with C)
From the standard [expr.const]/3:
A variable is usable in constant expressions after its initializing declaration is encountered if [...] it is a constant-initialized variable [...] of const-qualified integral or enumeration type.
This wouldn't extend to const auto t = test<true>{} because test<true> is not an integral type (You would need to have constexpr auto t = test<true>{}, as expected, following the rules of the rest of that paragraph)
In particular, how is this formalized?
http://eel.is/c++draft/expr.const#4.1
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
this ([expr.prim.this]), except in a constexpr function ([dcl.constexpr]) that is being evaluated as part of e;
Access to non-static members evaluates the this pointer. Access to a static member does not.
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
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.)