C++ 中的 constexpr switch 语句 [复制]

constexpr switch statements in C++ [duplicate]

提问人:mgNobody 提问时间:9/2/2023 最后编辑:mgNobody 更新时间:9/2/2023 访问量:107

问:

我们有C++支持吗?switch constexpr

对于上下文,我们都知道我们有编译时 if 语句。这些 if 语句的开销非常低,因为编译器将在编译时评估它们。我们(或者特别是)在进行模板专业化时会使用它们。if constexpr

我想知道我们是否有同样的东西。我有兴趣使用 switch 语句而不是具有相同运行时开销的长列表。switchif constexpr

例:

考虑以下代码:

template <int OP>
int mul(int in) {
  if constexpr (OP == 1) {
    return in * 2;
  } else if constexpr (OP == 2) {
    return in * 3;
  } else {
    return in * 4;
  }
}

以下代码在编译方面是否等同于上述代码?

template <int OP>
int mul(int in) {
  switch(OP) {
    case 1:
      return in * 2;
    case 2:
      return in * 3;
    default:
      return in * 4;
  }
}

这是指向上述示例链接的 Godbolt 链接。不知何故,该链接确认了我上面写的内容是等效的(就最终编译工件而言)。但是,我仍然对C++规范的正式确认感兴趣。

C++ 模板 if-constexpr

评论


答:

4赞 user17732522 9/2/2023 #1

以下代码在编译方面是否等同于上述代码?

代码具有完全相同的可观察行为。您只需要(或不存在的等效项)以允许未采用的分支包含如果使用模板专用化的模板参数实例化的代码,这些代码的格式不正确。if constexprswitch

任何关于优化或最终使用什么机器指令来实现指定的可观察行为的事情都取决于编译器,并且不会为任何一个版本指定。语言标准正式地只指定了编译器需要确保的程序的格式良好和允许的可观察行为,仅此而已。

因为甚至不会导致未获取分支的实例化,所以编译器当然可以确定不会为它发出任何指令,而在通常的情况下,您需要依靠优化来解决这个问题。但是,如果条件明显是编译时常量,那么编译器可能会发现其他分支是死代码,并将在足够高的优化级别删除它们。如果条件很复杂,并且不是明显的编译时常数,则情况可能会有所不同。if constexprswitch

评论

0赞 mgNobody 9/2/2023
是的,不知何故,在 if-constexpr 案例中编写格式不正确的代码是我的要求之一。我不仅对性能感兴趣,而且也对某种格式错误的代码感兴趣。
0赞 JaMiT 9/2/2023
@mgNobody “不知何故,在 if-constexpr 案例中编写格式不正确的代码是我的要求之一。”不过,它确实提到了“非常低的开销,因为编译器将在编译时评估它们”“相同的运行时开销”。我想你没有问你打算问的问题。由于已经回答了这个问题,据我了解,推荐的方法是在你提出的问题的背景下点赞/接受有用的答案,然后(如果仍然需要的话)提出一个专注于“格式不完善”的新问题。
1赞 user17732522 9/2/2023
@mgNobody 如果您打算在未采用的分支中编写格式可能不正确的代码,那么您只需要使用变体即可。否则,程序将无法编译。没有等价物,因此您别无选择,只能使用 // 链(或更复杂的东西,例如对 SFINAEed 函数重载的调用)。if constexprswitchif constexprelse if constexprelse
2赞 Blindy 9/2/2023 #2

你误解了什么是为了什么。它不是一个优化的推动者,因为你清楚地看到自己编译器会优化你的。if constexprif

相反,提供的是编写无效的 C++ 代码(可解析但格式不正确)的能力,这些代码仅在其自己的分支中有效(例如,如果您正在测试模板化类型),但总体上无效。if constexpr

评论

0赞 mgNobody 9/2/2023
是的,不知何故,在 if-constexpr 案例中编写格式不正确的代码是我的要求之一。我不仅对性能感兴趣,而且也对某种格式错误的代码感兴趣。
1赞 Blindy 9/3/2023
但不是.或者如果是,你不能这样做。请记住,一旦您进入分支,分支中的代码必须在编译时有效,并且由于 C++ 不允许基于类型的开关,因此纯数值测试不会向编译器提供任何其他信息。与C#相比,C#确实支持基于类型的开关(模式匹配),您可以在其中做您想做的事(尽管它不支持未触及的分支中的无效代码)。switch
-1赞 LiuYuan 9/2/2023 #3

以下代码在编译方面是否等同于上述代码?

在 gcc 中(即使在 7.1 等旧版本中,只要它支持模板),是的。

下面是测试代码。你可以发现编译器删除了所有的死分支,两个函数在汇编中是一样的。