如何在 constexpr 函数中实现回退运行时

How can I have a fallback run-time implementation in a constexpr function

本文关键字:实现 回退 运行时 函数 constexpr      更新时间:2023-10-16

上下文

我正在制作一个数学库,我需要使用sqrt().
由于sqrt()不是一个constexpr函数,我已经实现了constexpr版本的sqrt()和一个更快的std::sqrt()使用汇编,所以必须在运行时使用.
现在,我正在使用所有这些来计算向量的长度, 这个函数可以constexpr,因为我正在使结构中constexpr一切可能。

constexpr inline Real length(( const { return const_sqrt(lengthSquared(((;}

这将起作用,如果在非constexpr上下文中调用lenght()函数,它只是在运行时运行,但我的运行时实现sqrt()const_sqrt()更快。

问题

如何根据函数是在编译时还是运行时执行来切换为使用一种或另一种实现.
像这样:

constexpr inline Real length(( const { 返回IN_RUN_TIME  ?fast_sqrt(lengthSquared(((  : const_sqrt(lengthSquared(((; }

在 c++20 中,函数std::is_constant_evaluated可用于此目的:

constexpr Real length() const { 
return ! std::is_constant_evaluated()
? fast_sqrt(lengthSquared()) 
: const_sqrt(lengthSquared());
}

据我所知,在 c++20 之前,没有办法达到这种效果,这就是它被添加到语言中的原因。

另外,请注意,constexpr暗示inline因此关键字在这里是多余的。

唯一符合标准的解决方案是使用std::is_constant_evaluated,正如 cigien 所建议的那样:

constexpr inline Real length() const
{ 
return std::is_constant_evaluated()
? fast_sqrt<Real>(lengthSquared()) 
: const_sqrt<Real>(lengthSquared());
}

这种方法的问题在于,只有当length()的返回值用于初始化constexpr变量时,std::is_constant_evaluated()才会返回true,或者需要constexpr

在编译时已知lengthSquared()的值(因此可以使用const_sqrt(但不需要constexprlength()的返回值的情况下,这是次优的。然后is_constant_evaluated将返回false,因此将使用fast_sqrt,不必要地将1的计算推迟到运行时。

解决方法是使用非标准的GCC内置(也受Clang支持(:__builtin_constant_p。与std::is_constant_evaluated不同,它有一个"表达式"参数,并检查表达式的值在编译时是否已知(这可能取决于优化设置(。

我建议如果可用,应该使用__builtin_constant_p,否则回退到std::is_constant_evaluated。(如果您使用的是 C++20 之前的编译器,则此内置是您唯一的选择。

#ifdef __GNUC__ // Defined by GCC and Clang
#define KNOWN_AT_COMPILE_TIME(...) __builtin_constant_p(__VA_ARGS__)
#else
#define KNOWN_AT_COMPILE_TIME(...) std::is_constant_evaluated()
#endif
constexpr inline Real length() const
{ 
return KNOWN_AT_COMPILE_TIME(lengthSquared())
? fast_sqrt<Real>(lengthSquared()) 
: const_sqrt<Real>(lengthSquared());
}

1我假设fast_sqrt在编译时不起作用。否则,没有必要单独const_sqrt.