Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't an if constexpr make this core constant expression error disappear?

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).

like image 680
StoryTeller - Unslander Monica Avatar asked Oct 01 '17 12:10

StoryTeller - Unslander Monica


People also ask

Which is false about constexpr?

Short answer: static_assert(false) should never appear in a constexpr if expression, regardless of whether it's in a template function or whether it's in the discarded branch.

What is if constexpr in C++?

This is a big one! The static-if for C++! The feature allows you to discard branches of an if statement at compile-time based on a constant expression condition.

What is the benefit of constexpr?

A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations. And when a value is computed at compile time instead of run time, it helps your program run faster and use less memory.

What is a core constant expression?

Constant expression. A constant expression is either. a glvalue core constant expression that refers to. an object with static storage duration that is not a temporary, or. an object with static storage duration that is a temporary, but whose value satisfies the constraints for prvalues below, or.

What is a constexpr function in C++?

a constexpr function can only invoke other functions marked constexpr , and every expression involved (this includes creating objects) must be such that can appear in a constant-expression, and;

What is a constant expression in C++?

Constant expression. Literal constant expression is a prvalue core constant expression of non-pointer literal type (after conversions as required by context). A literal constant expression of array or class type requires that each subobject is initialized with a constant expression.

What is the difference between literal constant expression and reference constant expression?

A literal constant expression of array or class type requires that each subobject is initialized with a constant expression. Reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function.


2 Answers

The standard doesn't say much about the discarded statement of an if constexpr. There are essentially two statements in [stmt.if] about these:

  1. In an enclosing template discarded statements are not instantiated.
  2. 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>(); } 
like image 55
Dietmar Kühl Avatar answered Sep 22 '22 12:09

Dietmar Kühl


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

like image 29
songyuanyao Avatar answered Sep 23 '22 12:09

songyuanyao