【问题标题】:`if constexpr` vs `if` in light of compiler optimization and code performance`if constexpr` vs `if` 根据编译器优化和代码性能
【发布时间】:2019-02-06 01:44:03
【问题描述】:

考虑一个对性能非常关键的函数模板func。它可以用T=Type1 或其他类型来实例化。部分函数逻辑依赖于T,它被实例化了。

可以显式使用if constexpr(代码B)或使用原版if(代码A),而编译器可能会优化代码。

但是,我想知道,没有constexpr(代码A)的实现有什么不同?编译器是否能够检测if(在代码A中)的哪个分支在编译时在实例化时使用? 能否(对于代码 A)仍然生成效率较低的代码?

代码 A。没有 if constexpr:

template<class T>
void func(T argument)
{
    // some general type-independent logic
    if (std::is_same<Type1,T>::value)
    {
        // do something
    }
    else
    {
        // do something else
    }
    // some general type-independent logic
}

代码 B。 if constexpr:

template<class T>
void func(T argument)
{
    // some general type-independent logic
    if constexpr (std::is_same<Type1,T>::value)
    {
        // do something
    }
    else
    {
        // do something else
    }
    // some general type-independent logic
}

代码 A 和 B 都可以编译,因为 do somethingdo something else 对于任何 T 都是格式正确的。

有一些类似的问题:

如果出于某种原因代码 B 比代码 A 更可取(当两个分支都格式正确时),上述问题无法回答。

我看到的唯一好处是明确告诉程序员这个if 是编译时的;但是,我会说条件表达式是不言自明的。

【问题讨论】:

  • 主要基于意见。对于代码 B 比代码 A“更好”,或者代码 A 比代码 B“更好”,存在争论。这完全取决于对“更好”含义的具体和精确定义。唯一可以客观地说“B”优于“A”的情况是“A”会导致未定义的行为,而“B”不会。其他一切都可以抢夺。
  • 使用 B,编译后的代码中将没有分支,所以你已经为你准备好了。这很好。
  • 出于同样的原因,override 关键字存在,实际上最好有一个可编译但丢弃的分支,作为金丝雀,如果其他东西在其他一些头文件中发生更改,这将导致丢弃的分支不再编译,并就手头的问题发出警报。
  • @SamVarshavchik override [contextual] 关键字之所以存在,是因为当您打算覆盖时很容易隐藏签名错误。我不知道您在该评论的其余部分中谈论的是什么好处?请注意,if constexpr 的动机很大程度上在于丢弃的分支 并且 无法 编译 - 我们正在努力避免这种情况。
  • 如果不给出一个长示例,我无法更好地解释这一点,但有时您 /do/ 想要编译错误,如果由于 /not/ 附近的某些更改,上述“紧邻”不再编译,而不是被抑制在 if constexpr 中。可以这么说,胶囊总结是避免运行时错误的一种方法是安排(尽可能)将运行时错误转化为编译失败。

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


【解决方案1】:

if constexpr 不是为了优化。编译器非常擅长优化 if (true)if (false) 的分支(因为我们谈论的是常量表达式,所以归根结底就是这样)。这是 OP 中示例的 godbolt demo - 您会注意到 gcc 和 clang,即使在 -O0 上,也不会为简单的 if 发出分支。

if constexpr 旨在确保if 只有一个分支被实例化。这对于编写模板非常重要和有价值——因为现在我们实际上可以在同一个函数的主体中编写条件编译代码,而不是仅仅为了避免实例化而编写多个人工函数。

也就是说,如果您有一个已知常量表达式的条件 - 始终使用 if constexpr,无论您是否需要实例化优势。这样的决定没有缺点。它使读者更清楚地知道这个条件确实是恒定的(否则它甚至不会编译)。它还将强制将表达式评估为常量(slight variant 导致 gcc 在-O0 发出一个分支,而不是在-O1),随着即将添加的is_constant_evaluated() 可能在从长远来看(甚至可能否定我的开头段落)。


我看到的唯一好处是明确告诉程序员 this if 是编译时;但是,我会说条件表达式是不言自明的。

为了具体解决这个问题,是的,std::is_same&lt;X, Y&gt;::value 是“不言自明”的,它是一个常量表达式……因为我们碰巧熟悉std::is_same。但是,foo&lt;X&gt;::value 是常量表达式还是 foo&lt;X&gt;() + bar&lt;Y&gt;() 是常量表达式还是比这更复杂的任何东西都不太明显。

看到if constexpr 使得它在编译时不言自明,而不是条件本身的内容。

【讨论】:

  • if constexpr 在“不需要”时的最大优势是,如果你错误地认为它是 consteval,你会得到一个 error
猜你喜欢
  • 2015-04-15
  • 1970-01-01
  • 2020-06-09
  • 2013-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多