我正在制作一个数学库,需要使用sqrt()
。
由于sqrt()
不是一个constexpr
函数,我已经实现了一个sqrt()
的constexpr
版本,并且是一个更快的std::sqrt()
,它使用了程序集,因此必须在运行时使用。
现在,我使用所有这些来计算向量的长度,这个函数可以是constexpr
,因为我正在使
constexpr inline Real length() const { return const_sqrt<Real>(lengthSquared());}
如果在非constexpr
上下文中调用lenght()
函数,则该函数只在运行时运行,但我的sqrt()
运行时实现比const_sqrt()
快。
如何根据函数是在编译时还是在运行时执行切换到使用一种实现或另一种实现。
类似以下内容:
constexpr inline Real length() const { return IN_RUN_TIME ? fast_sqrt<Real>(lengthSquared()) : const_sqrt<Real>(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
变量,或者要求为ConstExpr
时,std::is_constant_evaluated()
才会返回true
。
当lengthsquared()
的值在编译时已知(因此可以使用const_sqrt
),但length()
的返回值不一定是constexpr
时,这是次优的。 则is_constant_evaluated
将返回false
,因此将使用fast_sqrt
,从而不必要地将计算推迟到1运行时。
解决方法是使用非标准GCC内置(也受Clang支持):__builtin_constant_p
。 与std::is_constant_evaluated
不同,它有一个“expression”参数,并在编译时检查表达式的值是否已知(这可能取决于优化设置)。
我建议使用__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
是没有意义的。