StoryTeller StoryTeller - 3 years ago 171
C++ Question

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

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

Answer Source

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>();
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download