编译器是否被迫根据模板参数评估 if 表达式?

Is the compiler forced to evaluate if expressions depending on template parameters?

考虑这个代码片段:

#include <iostream>

struct A {

    void f() { std::cout << "f"; }
    void g() { std::cout << "g"; }

    template <bool b>
    void call() { (b ? f() : g()); }
};

int main()
{
    A().call<true>();
}

根据传递给 calltemplate 参数,将调用 fg。我的问题是,如果编译器以某种方式被迫预先评估 if 条件。考虑 call:

的其他可能实现
template <bool b>
void call() { 
    bool x = b;    
    (x ? f() : g()); 
}

在这种情况下,编译时模板首先转换为运行时变量,然后再决定 fg。我假设,在这种情况下,编译器将无法删除 if 语句。

确实,如果我用 godbolt 测试这种行为,那似乎正是发生的情况。

标准中是否有某些部分定义了哪些模板表达式必须在编译时求值?

(当然,在这个简单的例子中,我可以轻松地为 b == trueb == false 编写特化,但这不是这个问题的目的。)

所有模板表达式都在编译时求值,因为模板在运行时不存在。您可以使用保证在编译时评估的 if constexpr

template <bool b>
void call() { if constexpr (b) f(); else g(); }

在你的例子中(b = true)这将变成

void call() { f(); }

不考虑优化级别。

然而,当您使用普通 if 或三元运算符时,生成的代码可能很容易优化。在您的示例中(再次针对 b=true)模板实例创建如下内容:

void call() { (true ? f() : g()); }

如果允许,编译器可以轻松优化。例如,带有 -O3 的 G++ 只是删除了 A 的实例和对 call 的调用,并且只是将 std::cout << "f" 移动到 main 中。见 godbolt.


结论:所有的模板表达式都是在编译时计算的,但是编译器可以识别表达式或变量是否为常量并能够优化这种情况,但编译器没有义务这样做(你不当您尝试调试某些东西时需要优化代码)