31

在参考这个问题。用于初始化constexpr变量的核心常量表达式y格式不正确。这么多是给定的。

但是,如果我尝试将其if变成if constexpr

template <typename T>
void foo() {
    constexpr int x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1 << x;
    }
}

int main(){
    foo<int>();
}

错误仍然存​​在。GCC 7.2 仍然提供:

error: right operand of shift expression '(1 << -1)' is negative [-fpermissive]

但我认为语义检查应该留在废弃的分支上。

但是,通过 lambda 进行间接constexpr处理确实有帮助:

template <typename T>
void foo(){
    constexpr int x = -1;
    constexpr auto p = []() constexpr { return x; };
    if constexpr (x >= 0){
        constexpr int y = 1<<p();
    }
}

on说明constexpry似乎改变了检查丢弃分支的方式。这是预期的行为吗?


@max66很友好地检查了其他实现。他报告说,GCC (7.2.0 / Head 8.0.0) 和 Clang (5.0.0 / Head 6.0.0) 都可以重现该错误。

4

3 回答 3

20

该标准并没有对 an的废弃语句if constexpr说明太多。[stmt.if] 中基本上有两个关于这些的声明:

  1. 在封闭模板中,丢弃的语句不会被实例化。
  2. 从废弃语句中引用的名称不需要定义 ODR。

这些都不适用于您的使用:编译器抱怨constexprif 初始化是正确的。请注意,当您想利用实例化失败时,您需要使条件依赖于模板参数:如果值不依赖于模板参数,则在定义模板时发生故障。例如,此代码仍然失败:

template <typename T>
void f() {
    constexpr int x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1<<x;
    }
}

但是,如果您x依赖于类型T,则可以,即使使用以下f实例进行实例化int

template <typename T>
void f() {
    constexpr T x = -1;
    if constexpr (x >= 0){
        constexpr int y = 1<<x;
    }
}
int main() {
    f<int>();
}
于 2017-10-01T12:57:25.910 回答
11

请注意,对于Consexpr If丢弃的语句:

对于每个可能的专业化,被丢弃的语句不能是格式错误的:

要解决此问题,您可以根据模板参数进行声明,例如

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;
    }
}

居住

于 2017-10-01T13:04:38.177 回答
6

我不确定您为什么希望不检查分支。根据[stmt.if]p2,if 分支唯一一次“未检查”是当它是模板的一部分且未实例化时:

在封闭模板实体的实例化期间(第 17 条),如果条件在其实例化后不依赖于值,则不实例化丢弃的子语句(如果有)。

您的代码似乎不适用于这种情况。

于 2017-10-01T12:50:07.530 回答