is_constant_evaluated() should produce constexpr variables? - c++

I have read the std::is_constant_evaluated() definition, but I still not sure why (1) is not working with the latest GCC: error: 'x' is not a constant expression
template<auto v>
struct s
{};
constexpr void f(int x)
{
if (std::is_constant_evaluated())
{
// constexpr int z=x; (1)
// s<x> a; (2)
}
}
int main(int argc, char* argv[])
{
f(4);
//f(argc);
return 0;
}
By the standard, should that work?
Or just the GCC implementation is buggy?
Somehow can I achieve the expected behavior? Which is basically:
With branching on std::is_constant_evaluated()
if it is true: the code can use variables as constexpr (like (2))
if it is false: the code use variables as non-constexpr
UPDATE
Can I 'transport' the constexpr-essiveness information into a function? Basically to decide in f() that it was call with constexpr x or not.
UPDATE
A more complex example about what I would like to achieve: this sample should stringify the parameter in compile time if possible.
template<auto v>
struct dummy_stringify
{
static constexpr auto str=v==4 ? "4" : "31"; // this is just an example; imagine here a much more complex logic
};
constexpr void f(int x)
{
if (std::is_constant_evaluated())
{
std::puts("A compile time calculation:");
//std::puts(dummy_stringify<x>::str);
} else
{
std::cout<<"A runtime calculation:"<<std::endl;
std::cout<<x<<std::endl;
}
}
int main(int argc, char* argv[])
{
f(4);
f(argc);
return 0;
}

x is not a constant expression, no matter how f itself is evaluated. That's a regular if right there (how is_constant_evaluated is meant to be used). It's not a discarded branch, so it has to contain well-formed code even when f is not constant evaluated. When x won't be a constant expression the function will still contain that (unexecuted) branch, and it will attempt to use x where a constant expression is required. That's plain ill-formed.
GCC is very much correct not to accept it.

The fundamental issue here is that, even with a constexpr (or even consteval) function being called during constant evaluation (and under an is_constant_evaluated check), there is still only one function shared among all argument values. You therefore can’t ever use a function parameter as a constant expression (even if the call with that parameter is a constant expression). If you want a constant-expression parameter, it has to be a template parameter.

UPDATE
I have found a solution with a little helper class (of course one can use std::integral_constant)
template<auto val_>
struct val
{
static constexpr auto value=val_;
};
template<auto v>
struct dummy_stringify
{
static constexpr auto str=v==4 ? "4" : "31"; // this is just an example; imagine here a much more complex logic
};
#include <iostream>
using namespace std;
template<class T>
constexpr void f(T x)
{
if constexpr(requires{ T::value; })
{
std::puts("A compile time calculation:");
std::puts(dummy_stringify<T::value>::str);
} else
{
std::cout<<"A runtime calculation:"<<std::endl;
std::cout<<x<<std::endl;
}
}
int main(int argc, char* argv[])
{
f(val<4>{});
f(argc);
return 0;
}
It can be improved with a template<char...> auto operator""_v(); to f(4_v)

Related

Why does min still complain inside constexpr?

I have the following code snippet:
#include <iostream>
#include <type_traits>
#include <algorithm>
#include <cstdint>
using T = double;
int main()
{
f();
}
void f() {
T x = 2;
if constexpr(std::is_integral_v<T>)
{
std::cout << std::min(static_cast<int64_t>(2), x);
} else {
std::cout << std::min(1.0, x);
}
}
The compiler is explaining that
<source>:15:57: error: no matching function for call to 'min(int64_t, T&)'
I thought it wouldn't be a problem because when T is a double, the first branch won't be instantiated. Apparently my understanding is wrong. Could someone help point out where my understanding goes wrong?
You need to make f() template, and T template parameter.
template <typename T>
void f() {
T x = 2;
if constexpr(std::is_integral_v<T>)
{
std::cout << std::min(static_cast<int64_t>(2), x);
} else {
std::cout << std::min(1.0, x);
}
}
then
int main()
{
f<double>();
}
For constexpr if:
(emphasis mine)
If a constexpr if statement appears inside a templated entity, and if
condition is not value-dependent after instantiation, the discarded
statement is not instantiated when the enclosing template is
instantiated .
Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive:
void f() {
if constexpr(false) {
int i = 0;
int *p = i; // Error even though in discarded statement
}
}
Outside of a template, the "false" branch of a constexpr if clause is discarded, not ignored. Thus, the code in such a branch must still be well-formed (and yours isn't, for the reason given).
From cppreference:
Outside a template, a discarded statement is fully checked. if constexpr
is not a substitute for the #if preprocessing directive.
std::min works with references. And both the arguments should be the same type. Since you're providing 2 different types, it cannot decide which one the argument type should be. You can work around that by explicitly specifying the type you like both the arguments to be converted:
std::min<double>(static_cast<int64_t>(2), x)
Be careful with dangling references.
The case when the failing branch of if constexpr doesn't matter is only in templates and f is not a template function.

Any way to move constexpr function that returns std::optional inside struct where it is used? [duplicate]

I am using g++4.8.0, which doesn't contain earlier constexpr bug. Thus below code works fine:
constexpr int size() { return 5; }
int array[size()];
int main () {}
However, if I enclose both the variable inside a class as static, then it gives compiler error:
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
int main () {}
Here is the error:
error: size of array ‘array’ is not an integral constant-expression
Is it forbidden to use constexpr in such a way or yet another g++ bug?
Yes, it is ill-formed. Here's why:
A constexpr function needs to be defined (not just declared) before being used in a constant expression.
So for example:
constexpr int f(); // declare f
constexpr int x = f(); // use f - ILLEGAL, f not defined
constexpr int f() { return 5; } // define f, too late
function definitions inside a class specifier (as well as initializers and default parameters) are essentially parsed in an order like they were defined outside the class.
So this:
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
Is parsed in this order:
struct X {
constexpr inline static int size(); // function body defered
static const int array[size()]; // <--- POINT A
};
constexpr inline int X::size() { return 5; }
That is, parsing of function bodies are defered until after the class specifier.
The purpose of this deferral of function body parsing is so that function bodies can forward reference class members not yet declared at that point, and also so they can use their own class as a complete type:
struct X
{
void f() { T t; /* OK */ }
typedef int T;
};
Compared to at namespace scope:
void f() { T t; /* error, T not declared */ }
typedef int T;
At POINT A, the compiler doesn't have the definition of size() yet, so it can't call it. For compile-time performance constexpr functions need to be defined ahead of their use in the translation unit before being called during compile, otherwise the compiler would have to make a multiple passes just to "link" constant expressions for evaluation.
Apparently it's not even a bug, because its status is RESOLVED INVALID, which means that the people behind GCC and that bugzilla, after reviewing the problem, don't think that this is a GCC bug.
I remind you on that page because there is also an answer for this behaviour in one of the related posts.
I just wanted to add that even though this may not be good practice and will restrict you to defining the class body in the same compilation unit that its declared, it's possible to force the compiler to compile the definition of the function bodies at the same point as its declaration by adding a redundant template parameter:
template <typename = void>
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
int main()
{
X<> x{};
...
}

Nesting contexpr functions error

The follwing code doesn't compile with g++/clang++.
constexpr int bar(int v) {
if (v > 0){
return v * 2;
}
return 2;
}
constexpr int foo(const int v) {
constexpr auto x = bar(v); // error
return v;
}
int main() {
constexpr auto a = foo(1);
constexpr auto b = bar(1); // ok
}
The error message is: x must be initailized by a constant expression
But from the line (ok) you see that bar() is constexpr.
if I change the body of foo() to
constexpr int foo(const int v) {
return bar(v);
}
its ok!
I don't get this clear, why the first form isn't possilble.
I used g++-6.2.1, g++-7.0.0 and clang++-3.9.0
The fix is this
constexpr int foo(const int v) {
auto x = bar(v);
return v;
}
The keyword constexpr means two very slightly different things. A constexpr variable must be evaluated at compile time, whereas a constexpr function must be possible to evaluate at compile time. There is nothing to prevent you from calling foo at runtime. This means...
The argument v is not necessarily constexpr.
When bar is called with b the answer might not be constexpr.
The result of cannot be stored in a constexpr variable as it might not be constexpr.
If foo is called at compile time then x is not stored, it is a temporary variable within the compiler, so making it constexpr doesn't make any sense.
The constexpr'ness of x can only make sense if foo is evaluated at runtime, in which case it cannot be constexpr, which cases an error.

template static constexpr definition of odr-used variable

Actual code is more complex but I was able to reduce it to this example.
Everything works fine until I try to take a pointer to MyPackets_t::types (uncomment call to foo() in main() )
At this point in order for the application to link, types requires a definition.
I'm struggling with correct syntax for the definition. Commented out template... should do the trick. However it generetes an error " template arguments to 'PacketCollection::types' do not match original template".
Trying something like this - template<> constexpr int PacketCollection::types[]; - causes another linker error as though variable is being used in that line rather than declared.
I've tried using MyPackets_t instead of PacketCollection - same results.
If I make packet collection non-templated then everything compiles and runs as expected.
I feel that I'm either missing something extremely basic here or there is a bug in the compiler. I get this behavior with gcc 4.8 and 4.9.
Clang 3.5 has a sligtly different take on the situation: declaration of constexpr static data member 'types' requires an initializer.
The only workaround that I've found so far was to use
static constexpr std::array<int, 2> types() { return {1,2}; }
instead, but I don't like this solution. If variable workes in non-templated version (using the initializer from header) it should also work for templated one.
#include <iostream>
using namespace std;
class Packet_A
{
public:
static constexpr int TYPE = 1;
};
class Packet_B
{
public:
static constexpr int TYPE = 2;
};
template <typename T> class PacketCollection
{
public:
static constexpr size_t size = 2;
static constexpr int types[size] { 1, 2 };
};
typedef PacketCollection<Packet_A> MyPackets_t;
//template<typename T> constexpr int PacketCollection<Packet_A>::types[PacketCollection<Packet_A>::size];
void foo(const int* bar, size_t size)
{
if (size >= 2)
{
cout << bar[0] << bar[1];
}
}
int main(int argc, char* argv[])
{
cout << Packet_A::TYPE;
cout << MyPackets_t::types[0];
//foo(MyPackets_t::types, MyPackets_t::size);
return 0;
}
You should use T instead of Packet_A
template<typename T> constexpr int PacketCollection<T>::types[PacketCollection<T>::size];
^ ^
See live example.

Enforce compile-time constexpr [duplicate]

This question already has answers here:
How to ensure constexpr function never called at runtime?
(5 answers)
Closed 1 year ago.
In C++11 we get constexpr:
constexpr int foo (int x) {
return x + 1;
}
Is it possible to make invocations of foo with a dynamic value of x a compile time error? That is, I want to create a foo such that one can only pass in constexpr arguments.
Replace it with a metafunction:
template <int x> struct foo { static constexpr int value = x + 1; };
Usage:
foo<12>::value
Unfortunately, there is no way guarantee that a constexpr function, even the most trivial one, will be evaluated by the compiler unless absolutely necessary. That is, unless it appears in a place where its value is required at compile time, e.g. in a template. In order to enforce the compiler to do the evaluation during compilation, you can do the following:
constexpr int foo_implementation (int x) {
return x + 1;
}
#define foo(x) std::integral_constant<int, foo_implementation(x)>::value
and then use foo in your code as usual
int f = foo(123);
The nice thing about this approach is that it guarantees compile-time evaluation, and you'll get a compilation error if you pass a run-time variable to foo:
int a = 2;
int f = foo(a); /* Error: invalid template argument for 'std::integral_constant',
expected compile-time constant expression */
The not so nice thing is that it requires a macro, but this seems currently inevitable if you want both guaranteed compile-time evaluation and pretty code. (I'd love to be proven wrong though!)
Yes, it can now be done in purely idiomatic C++, since C++20 added support for this kind of problem. You annotate a function with consteval and can be sure that it's being evaluated during compile time. https://en.cppreference.com/w/cpp/language/consteval
consteval int foo( int x ) {
return x + 1;
}
int main( int argc, char *argv[] )
{
return foo( argc ); // This will not compile
return foo( 1 ); // This works
}
Also see this godbolt.org demo in the 3 most relevant compilers.
I would use static_assert as shown in this example
#include<iostream>
constexpr int foo(int x) {
return x+1;
}
int main() {
// Works since its static
std::cout << foo(2) << std::endl;
static_assert(foo(2) || foo(2) == 0, "Not Static");
// Throws an compile error
int in = 3;
std::cout << foo(in) << std::endl;
static_assert(foo(in) || foo(in) == 0, "Not Static");
}
For more infos: http://en.cppreference.com/w/cpp/language/static_assert