为什么if constexpr不会使这个核心常量表达式错误消失?

时间:2017-10-01 12:08:28

标签: c++ language-lawyer c++17 constexpr if-constexpr

参考this question。用于初始化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]

但我认为语义检查应该在废弃的分支上保持原状。

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

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

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

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

3 个答案:

答案 0 :(得分:20)

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

  1. 在封闭模板中,不会实例化丢弃的语句。
  2. 从废弃的语句引用的名称不需要定义ODR。
  3. 这些都不适用于您的使用:编译器在初始化时抱怨constexpr是正确的。请注意,当您希望利用实例化失败时,您需要使条件依赖于模板参数:如果该值不依赖于模板参数,则在模板已定义。例如,此代码仍然失败:

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

答案 1 :(得分:11)

请注意,对于Constexpr 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;
    }
}

LIVE

答案 2 :(得分:6)

我不确定为什么你不期望检查分支。唯一一次if分支是&#34;未检查&#34;是什么时候它是模板的一部分,而不是实例化,根据[stmt.if] p2:

  

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

您的代码似乎没有适用的情况。