In reference to this question. The core constant expression that is used to initialize the constexpr variable y is ill-formed. So much is a given.
But if I try to turn the if into an if constexpr:
template <typename T>
void foo() {
constexpr int x = -1;
if constexpr (x >= 0){
constexpr int y = 1 << x;
}
}
int main(){
foo<int>();
}
The error persists. With GCC 7.2 still giving:
error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]
But I thought that the semantic check should be left unpreformed on a discarded branch.
Making an indirection via a constexpr lambda does help, however:
template <typename T>
void foo(){
constexpr int x = -1;
constexpr auto p = []() constexpr { return x; };
if constexpr (x >= 0){
constexpr int y = 1<<p();
}
}
The constexpr specifier on y seems to alter how the discarded branch is checked. Is this the intended behavior?
#max66 was kind enough to check other implementations. He reports that the error is reproducible with both GCC (7.2.0 / Head 8.0.0) and Clang (5.0.0 / Head 6.0.0).
The standard doesn't say much about the discarded statement of an if constexpr. There are essentially two statements in [stmt.if] about these:
In an enclosing template discarded statements are not instantiated.
Names referenced from a discarded statement are not required ODR to be defined.
Neither of these applies to your use: the compilers are correct to complain about the constexpr if initialisation. Note that you'll need to make the condition dependent on a template parameter when you want to take advantage of the instantiation to fail: if the value isn't dependent on a template parameter the failure happens when the template is defined. For example, this code still fails:
template <typename T>
void f() {
constexpr int x = -1;
if constexpr (x >= 0){
constexpr int y = 1<<x;
}
}
However, if you make x dependent on the type T it is OK, even when f is instantiated with int:
template <typename T>
void f() {
constexpr T x = -1;
if constexpr (x >= 0){
constexpr int y = 1<<x;
}
}
int main() {
f<int>();
}
Note that for the statement discarded by Constexpr If:
the discarded statement can't be ill-formed for every possible specialization:
To fix the issue you can make the statement depending on the template parameter, e.g.
template<typename T, int X> struct dependent_value { constexpr static int V = X; };
template <typename T>
void foo() {
constexpr int x = -1;
if constexpr (x >= 0){
constexpr int y = 1 << dependent_value<T, x>::V;
}
}
LIVE
I'm not sure why you expect the branch to not be checked. The only time an if branch is "not checked" is when it is part of a template and not instantiated, as per [stmt.if]p2:
During the instantiation of an enclosing templated
entity (Clause 17), if the condition is not value-dependent after its instantiation, the discarded substatement
(if any) is not instantiated.
Your code doesn't seem to be in a situation where this applies.
Related
I was messing around with c++20 consteval in GCC 10 and wrote this code
#include <optional>
#include <tuple>
#include <iostream>
template <std::size_t N, typename Predicate, typename Tuple>
consteval std::optional<std::size_t> find_if_impl(Predicate&& pred,
Tuple&& t) noexcept {
constexpr std::size_t I = std::tuple_size_v<std::decay_t<decltype(t)>> - N;
if constexpr (N == 0u) {
return std::nullopt;
} else {
return pred(std::get<I>(t))
? std::make_optional(I)
: find_if_impl<N - 1u>(std::forward<decltype(pred)>(pred),
std::forward<decltype(t)>(t));
}
}
template <typename Predicate, typename Tuple>
consteval std::optional<std::size_t> find_if(Predicate&& pred,
Tuple&& t) noexcept {
return find_if_impl<std::tuple_size_v<std::decay_t<decltype(t)>>>(
std::forward<decltype(pred)>(pred), std::forward<decltype(t)>(t));
}
constexpr auto is_integral = [](auto&& x) noexcept {
return std::is_integral_v<std::decay_t<decltype(x)>>;
};
int main() {
auto t0 = std::make_tuple(9, 1.f, 2.f);
constexpr auto i = find_if(is_integral, t0);
if constexpr(i.has_value()) {
std::cout << std::get<i.value()>(t0) << std::endl;
}
}
Which is supposed to work like the STL find algorithm but on tuples and instead of returning an iterator, it returns an optional index based on a compile time predicate. Now this code compiles just fine and it prints out
9
But if the tuple does not contain an element that's an integral type, the program doesn't compile, because the i.value() is still called on an empty optional. Now why is that?
This is just how constexpr if works. If we check [stmt.if]/2
If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool; this form is called a constexpr if statement. If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity ([temp.pre]), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.[...]
emphasis mine
So we can see that we only do not evaluate the discarded expression if we are in a template and if the condition is value-dependent. main is not a function template so the body of the if statement is still checked by the compiler for correctness.
Cppreference also says this in their section about constexpr if with:
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 .
template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs) {
// ... handle p
if constexpr (sizeof...(rs) > 0)
g(rs...); // never instantiated with an empty argument list.
}
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
}
}
I have an enum like:
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
}
And I want to create a compile-time mapping to get the appropriate E for a type like:
GetE<float> // returns TYPE_FLOAT
GetE<char> // returns TYPE_CHAR
GetE<int> // returns TYPE_INT
I thought of:
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
But I'm getting errors like:
undefined reference to `GetE<int>::type'
Whats the best way to do this? And why the error?
It depends on how you use these constant expressions.
The ODR (one-definition rule) states that
(§3.2/2) [...] A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. [...]
(And then, lots of special rules, exceptions and exceptions of the exceptions follow.)
Any variable that is odr-used, must have exactly one definition. Your constant expressions have a declaration, but not a definition, so this goes well unless you odr-use one of them.
For example, the following goes well:
int main() {
E e = GetE<float>::type;
return 0;
}
But this does not:
void f(const E &)
{ }
int main() {
f(GetE<float>::type);
return 0;
}
because f requires a (const) reference, so the lvalue-to-rvalue conversion cannot be applied immediately, hence this constitutes an odr-use. The compiler will complain that it misses a definition.
(Remark. As ShafikYaghmour found (see the comments), you may not get a complaint if the compiler uses optimization, as the references may be optimized away. To reproduce the compiler complaint, use the -O0 flag (or similar, depending on the compiler).)
To solve the problem, the required definition can be provided in the usual way, i.e. outside the struct-definition:
constexpr E GetE<float>::type;
constexpr E GetE<char>::type;
constexpr E GetE<int>::type;
But since this would have to happen in the .cpp (not the header file), you'll end up having to maintain the declarations and definitions in two different places, which is cumbersome.
The solution you've just suggested in your comment, i.e. define a constexpr (and inline) function, sounds right:
template <class T> constexpr E GetE();
template <> constexpr E GetE<float>()
{ return TYPE_FLOAT; }
template <> constexpr E GetE<char>()
{ return TYPE_CHAR; }
template <> constexpr E GetE<int>()
{ return TYPE_INT; }
void f(const E &)
{ }
int main() {
E e = GetE<float>();
f(GetE<float>());
return 0;
}
Static member variables need to be defined outside the class scope:
class C {
const static int x = 5;
};
decltype(C::x) C::x;
Maybe because you forgot to put a semicolon after the enum definition, this works for me in LiveWorkSpace:
#include <iostream>
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
} ;
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
int main()
{
std::cout << GetE<int>::type << std::endl ;
}
here is a link to the code http://liveworkspace.org/code/nHqUe$6
Consider the following code that implements a compile time counter.
#include <iostream>
template<int>
struct Flag { friend constexpr int flag(Flag); };
template<int N>
struct Writer
{
friend constexpr int flag(Flag<N>) { return 0; }
};
template<int N>
constexpr int reader(float, Flag<N>) { return N; }
template<int N, int = flag(Flag<N>{})>
constexpr int reader(int, Flag<N>, int value = reader(0, Flag<N + 1>{}))
{
return value;
}
template<int N = reader(0, Flag<0>{}), int = sizeof(Writer<N>) >
constexpr int next() { return N; }
int main() {
constexpr int a = next();
constexpr int b = next();
constexpr int c = next();
constexpr int d = next();
std::cout << a << b << c << d << '\n'; // 0123
}
For the second reader overload, if I put the default parameter inside the body of the function, like so:
template<int N, int = flag(Flag<N>{})>
constexpr int reader(int, Flag<N>)
{
return reader(0, Flag<N + 1>{});
}
Then the output will become:
0111
Why does this happen? What makes the second version not work anymore?
If it matters, I'm using Visual Studio 2015.2.
Without value being passed as a parameter, nothing stops the compiler from caching the call to reader(0, Flag<1>).
In both cases first next() call will work as expected since it will immediately result in SFINAEing to reader(float, Flag<0>).
The second next() will evaluate reader<0,0>(int, ...), which depends on reader<1>(float, ...) that can be cached if it does not depend on a value parameter.
Unfortunately (and ironically) the best source I found that confirms that constexpr calls can be cached is #MSalters comment to this question.
To check if your particular compiler caches/memoizes, consider calling
constexpr int next_c() { return next(); }
instead of next(). In my case (VS2017) the output turns into 0000.
next() is protected from caching by the fact that its default template arguments actually change with every instantiation, so it's a new separate function every time. next_c() is not a template at all, so it can be cached, and so is reader<1>(float, ...).
I do believe that this is not a bug and compiler can legitimately expect constexprs in compile-time context to be pure functions.
Instead it is this code that should be considered ill-formed - and it soon will be, as others noted.
The relevance of value is that it participates in overload resolution. Under SFINAE rules, template instantiation errors silently exclude candidates from overload resolution. But it does instantiate Flag<N+1>, which causes the overload resolution to become viable the next time (!). So in effect you're counting successful instantiations.
Why does your version behave differently? You still reference Flag<N+1>, but in the implementation of the function. This is important. With function templates, the declaration must be considered for SFINAE, but only the chosen overload is then instantiated. Your declaration is just template<int N, int = flag(Flag<N>{})> constexpr int reader(int, Flag<N>); and does not depend on Flag<N+1>.
As noted in the comments, don't count on this counter ;)
I am working with a code example concerning stateful meta programming.
The original code example can be found from this link: http://b.atch.se/posts/non-constant-constant-expressions/#appendix-clang-workaround
In order to understand this technique, I try to modify the code example each time a bit. Here follows the latest version which still works:
constexpr int adl_flag(int);
template <class Tag> struct writer {
friend constexpr int adl_flag(int) {
return 0;
}
};
template <int = adl_flag(0)> constexpr bool is_flag_usable(int) {
return true;
}
constexpr bool is_flag_usable (...) {
return false;
}
template <
class Tag = int,
bool B = is_flag_usable(0),
int = sizeof (writer<Tag>) // replace Tag with int
>
constexpr int f() {
return B;
}
int main() {
constexpr int a = f();
constexpr int b = f();
static_assert(a != b, "fail");
return 0;
}
To me, it seems trivial to replace Tag with int. But this replacement in fact make the static_assert fail.
I guess this is because the compiler no longer implicitly instantiate f for the second call :
constexpr int b = f();
But the template type parameter Tag seems has nothing to do with this. Can somebody explain what's actually going on here, please.
The compiler I used is g++ 5.4.1. Compiling with -std=gnu++14.
sizeof(writer<int>) is non-dependent. That means that wrapper<int> is found in first phase lookup, when f is parsed (once).
sizeof(writer<Tag>), on the other hand, is dependent on Tag. Thus, its lookup is deferred until phase two, when f is instantiated (at each of its call sites).
I have an enum like:
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
}
And I want to create a compile-time mapping to get the appropriate E for a type like:
GetE<float> // returns TYPE_FLOAT
GetE<char> // returns TYPE_CHAR
GetE<int> // returns TYPE_INT
I thought of:
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
But I'm getting errors like:
undefined reference to `GetE<int>::type'
Whats the best way to do this? And why the error?
It depends on how you use these constant expressions.
The ODR (one-definition rule) states that
(§3.2/2) [...] A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. [...]
(And then, lots of special rules, exceptions and exceptions of the exceptions follow.)
Any variable that is odr-used, must have exactly one definition. Your constant expressions have a declaration, but not a definition, so this goes well unless you odr-use one of them.
For example, the following goes well:
int main() {
E e = GetE<float>::type;
return 0;
}
But this does not:
void f(const E &)
{ }
int main() {
f(GetE<float>::type);
return 0;
}
because f requires a (const) reference, so the lvalue-to-rvalue conversion cannot be applied immediately, hence this constitutes an odr-use. The compiler will complain that it misses a definition.
(Remark. As ShafikYaghmour found (see the comments), you may not get a complaint if the compiler uses optimization, as the references may be optimized away. To reproduce the compiler complaint, use the -O0 flag (or similar, depending on the compiler).)
To solve the problem, the required definition can be provided in the usual way, i.e. outside the struct-definition:
constexpr E GetE<float>::type;
constexpr E GetE<char>::type;
constexpr E GetE<int>::type;
But since this would have to happen in the .cpp (not the header file), you'll end up having to maintain the declarations and definitions in two different places, which is cumbersome.
The solution you've just suggested in your comment, i.e. define a constexpr (and inline) function, sounds right:
template <class T> constexpr E GetE();
template <> constexpr E GetE<float>()
{ return TYPE_FLOAT; }
template <> constexpr E GetE<char>()
{ return TYPE_CHAR; }
template <> constexpr E GetE<int>()
{ return TYPE_INT; }
void f(const E &)
{ }
int main() {
E e = GetE<float>();
f(GetE<float>());
return 0;
}
Static member variables need to be defined outside the class scope:
class C {
const static int x = 5;
};
decltype(C::x) C::x;
Maybe because you forgot to put a semicolon after the enum definition, this works for me in LiveWorkSpace:
#include <iostream>
enum E
{
TYPE_FLOAT,
TYPE_CHAR,
TYPE_INT
} ;
template<class T> struct GetE;
template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; };
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; };
template<> struct GetE<int> { static constexpr E type = TYPE_INT; };
int main()
{
std::cout << GetE<int>::type << std::endl ;
}
here is a link to the code http://liveworkspace.org/code/nHqUe$6