C++ `根据编译器优化和代码性能,if constexpr`vs`if`

C++ `根据编译器优化和代码性能,if constexpr`vs`if`,c++,c++17,if-constexpr,C++,C++17,If Constexpr,考虑一个性能非常关键的函数模板func。它可以用T=Type1或其他类型实例化。函数逻辑的一部分取决于它被实例化的T 可以显式使用if constexpr(代码B),也可以使用普通的if(代码a),而编译器可能会优化代码 然而,我想知道,没有constexpr(代码A)的实现有什么不同?编译器是否能够在实例化时在编译时检测if(在代码A中)的哪个分支它(对于代码A)仍然可以生成效率较低的代码吗 代码A.如果constexpr,则不带: template<class T> void f

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

可以显式使用
if constexpr
(代码B),也可以使用普通的
if
(代码a),而编译器可能会优化代码

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

代码A.如果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
}
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
}
对于任何
T
,代码A和代码B都是编译的,因为
做某事
做其他事情
格式良好

有一些听起来类似的问题:

  • –当需要
    constexpr
    时,此选项会进行应答
  • –只列出差异
如果出于某种原因代码B比代码A更可取(当两个分支的格式都很好时),上述问题无法回答


我看到的唯一优势是明确地告诉程序员,如果
是编译时,那么这
;但是,我认为条件表达式是自解释的。

如果constexpr
不是为了优化。编译器非常擅长优化分支,即
if(true)
if(false)
(因为我们讨论的是常量表达式,这就是它的本质)。这里是OP中的一个示例-您会注意到,即使在
-O0
上,gcc和clang都不会为简单的
if
发出分支


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

也就是说,如果您有一个已知常量表达式的条件-只要始终使用
if constexpr
,无论您是否需要实例化好处。这样的决定没有坏处。它让读者更清楚地认识到,这个条件确实是常量(否则它甚至无法编译)。它还将强制将表达式作为常量进行求值(a导致gcc在
-O0
处发出一个分支,认为不是在
-O1
),随着
的加入,从长远来看,它可能变得更为重要(甚至可能否定我的开头段落)


我看到的唯一优势是明确地告诉程序员这是编译时;然而,我想说条件表达式是不言自明的

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


它看到的是
if constexpr
,这使得编译时它是自解释的,而不是条件本身的内容。

主要基于观点。有人认为代码B比代码A“更好”,或者代码A比代码B“更好”。这完全取决于“更好”的含义的具体而精确的定义。只有当“A”导致未定义的行为,而“B”不是时,你才能客观地说“B”比“A”好。其他的一切都可以得到,有了B,编译后的代码中就没有分支了,所以你就有了分支。这很好。出于同样的原因,
override
关键字也存在,最好有一个可编译但被丢弃的分支,当其他头文件中的某些内容发生更改时,该分支将充当金丝雀,从而导致被丢弃的分支不再编译,并就手头的问题发出警报。@SamVarshavchik存在
覆盖
[context]关键字,因为当您仅通过错误获取签名的某些部分来覆盖时,很容易隐藏该关键字。我不知道你在剩下的评论里说的是什么好处?请注意,
if constexpr
的动机很大程度上是因为被丢弃的分支没有也无法编译-我们正试图避免这一点。如果不给出一个很长的示例,我无法更好地解释这一点,但有时您/确实/想要编译错误,如果由于附近的某个更改而导致,前面提到的“紧邻”不再编译,而不是在
if constexpr
中被抑制。只需说一句,capsule摘要是避免运行时错误的一种方法是(尽可能地)安排将运行时错误转化为编译失败。if constexpr在“不需要”时的最大优点是,如果您错误地认为它是consteval,则会得到一个错误。