在 CppNow 演讲中(Don’t constexpr All the Things - David Sankel),它使用以下函数作为示例:
constexpr int f()
{
if constexpr (std::is_constant_evaluated()) {
// slow version at runtime but that can be evaluated at compile-time
} else {
// fast version at runtime
}
}
David 说,在这个例子中,函数的定义取决于
std::is_constant_evaluated()
,因此函数在运行时将始终“慢”或在运行时始终“快”。
但同时,
std::is_constant_evaluated()
是true
还是false
取决于使用的上下文,对吧?例如:
const int i = f();
在此示例中,编译将尝试在编译时计算常量
i
的值。为此,编译器调用 f
并将 is_constant_evaluated
设置为 true
来查看会发生什么。如果它可以在编译时评估结果,它就会执行此操作并在编译时设置 i
的最终值。
但是,从其他上下文中,可以将
f()
设置为 std::is_constant_evaluated
来调用 false
。
因此,如果
std::is_constant_evaluated
根据“调用者上下文”设置为 true
或 false
,因此两种可能性都发生在代码中的不同点,那么“被调用者”怎么可能只有一个定义呢?
抱歉,如果我的问题没有明确定义,但有些东西我还不太明白,我现在很困惑。
David 说,在这个例子中,函数的定义依赖于 std::is_constant_evaluated()
他是说,当
if constexpr
被实例化时,它将仅“打开”两个分支之一,这意味着实例化函数实际上只会使用两个分支之一。
在幻灯片上有一个小错误,考虑到他如何谈论该函数,他可能意味着它是一个函数模板,即开头缺少一个
template<typename T>
或类似的内容。他甚至提到它“通常用于模板”。
但是无论是否是模板,结果都是一样的。
if constexpr
的条件必须是本身常量表达式。因此,无论何时评估它,它都是在编译时而不是运行时评估的。因此,在 std::is_constant_evaluated()
条件下,true
将始终返回 if constexpr
。
因此,无论调用者上下文如何,都永远无法采用快速分支,并且如果这是一个模板,它甚至不会被实例化。
这就是为什么
std::is_constant_evaluated
条件中的 if constexpr
是 总是 错误。
使用 C++23,您可以忘记
std::is_constant_evaluated
的存在。相反,你可以写总是写
if consteval
{
// compile-time branch
}
else
{
// runtime branch
}
if consteval
做了 std::is_constant_evaluated()
应该做得更好的事情,并且不允许出现此类错误。