【问题标题】:Why doesn't an if constexpr make this core constant expression error disappear?为什么 if constexpr 不会使这个核心常量表达式错误消失?
【发布时间】:2018-03-12 17:47:53
【问题描述】:

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

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


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

【问题讨论】:

  • 听起来像一个编译器错误。您是否尝试过其他实现?
  • @ShacharShemesh - 我自己没有。但是我链接的帖子的 OP 报告了 Clang and MSVC behave the same.
  • 我确认了 clang++ 3.8.1 的问题
  • 来自 wanbox 我看到问题仍然存在于 g++(7.2.0 和 Head 8.0.0)和 clang++(5.0.0 和 Head 6.0.0)。
  • 我不认为“y 上的 constexpr 说明符似乎改变了丢弃分支的检查方式”,因为删除了 constexpr(简单地定义“int y = 1 y 是初始化运行时),但我看到一个警告(未使用的变量y);所以(如果我没记错的话)该分支已编译。或者更好,我认为正确的意思是 constexpr y 被初始化编译时(所以 1 &lt;&lt; x 被立即检查)并且没有被初始化运行时。

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


【解决方案1】:

标准对if constexpr废弃声明 没有太多说明。 [stmt.if] 中有两个关于这些的声明:

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

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

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

但是,如果您使x 依赖于T 类型,则可以,即使fint 实例化:

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

【讨论】:

  • 当我对 Kerrek 发表评论时,我帖子中的示例很糟糕。在将代码移动到模板后,我确实验证了它是否仍然存在。基本情况仍然会发出错误。间接没有。我已经更新了 MCVE。很抱歉给您带来不便。
  • @StoryTeller:我更新了我的回复以澄清只有模板的 实例化 受到影响。你得到的错误发生在模板的定义期间!
  • @DietmarKühl:很好的一点——检查非依赖结构是模板定义的一部分,而不是任何模板实例化的一部分,所以废弃臂的非实例化实际上是一个红色鲱鱼。
  • 嗯.. 是的,我开始明白我的理解哪里出了问题。 I also forgot that if no template specialization is valid, for any argument, the program is ill-formed no diagnostic required。这完全解决了我的困惑。
【解决方案2】:

注意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

【讨论】:

    【解决方案3】:

    我不确定您为什么希望不检查分支。根据 [stmt.if]p2:

    ,if 分支唯一“未检查”的情况是它是模板的一部分而不是实例化

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

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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-13
    • 1970-01-01
    • 1970-01-01
    • 2014-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    相关资源
    最近更新 更多