编译器是否被迫根据模板参数评估 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>();
}
根据传递给 call
的 template
参数,将调用 f
或 g
。我的问题是,如果编译器以某种方式被迫预先评估 if 条件。考虑 call
:
的其他可能实现
template <bool b>
void call() {
bool x = b;
(x ? f() : g());
}
在这种情况下,编译时模板首先转换为运行时变量,然后再决定 f
或 g
。我假设,在这种情况下,编译器将无法删除 if 语句。
确实,如果我用 godbolt 测试这种行为,那似乎正是发生的情况。
标准中是否有某些部分定义了哪些模板表达式必须在编译时求值?
(当然,在这个简单的例子中,我可以轻松地为 b == true
和 b == 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.
结论:所有的模板表达式都是在编译时计算的,但是编译器可以识别表达式或变量是否为常量并能够优化这种情况,但编译器没有义务这样做(你不当您尝试调试某些东西时需要优化代码)
考虑这个代码片段:
#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>();
}
根据传递给 call
的 template
参数,将调用 f
或 g
。我的问题是,如果编译器以某种方式被迫预先评估 if 条件。考虑 call
:
template <bool b>
void call() {
bool x = b;
(x ? f() : g());
}
在这种情况下,编译时模板首先转换为运行时变量,然后再决定 f
或 g
。我假设,在这种情况下,编译器将无法删除 if 语句。
确实,如果我用 godbolt 测试这种行为,那似乎正是发生的情况。
标准中是否有某些部分定义了哪些模板表达式必须在编译时求值?
(当然,在这个简单的例子中,我可以轻松地为 b == true
和 b == 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.
结论:所有的模板表达式都是在编译时计算的,但是编译器可以识别表达式或变量是否为常量并能够优化这种情况,但编译器没有义务这样做(你不当您尝试调试某些东西时需要优化代码)