'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).
Solution 1:[1]
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>();
}
Solution 2:[2]
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;
}
}
Solution 3:[3]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Potatoswatter |
| Solution 2 | songyuanyao |
| Solution 3 | Kerrek SB |
