4

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

可以显式使用if constexpr(代码 B)或使用 vanillaif代替(代码 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 something并且do something else对于任何T.

有一些类似的问题:

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

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

4

1 回答 1

11

if constexpr不是为了优化。编译器非常擅长优化分支是if (true)or if (false)(因为我们正在谈论常量表达式,这就是它归结为的内容)。这是 OP 中示例的godbolt 演示- 你会注意到 gcc 和 clang,即使是 on -O0,也不会为简单的if.

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

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


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

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

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

于 2019-02-06T02:13:57.783 回答