我怎样才能在 constexpr 函数中有回退 运行-time 实现
How can I have a fallback run-time implementation in a constexpr function
上下文
我正在制作一个数学库,我需要使用 sqrt()
。
由于 sqrt()
不是 constexpr
函数,我已经实现了 sqrt()
的 constexpr
版本,并且比使用汇编的 std::sqrt()
更快,因此必须在运行-时间。
现在,我正在使用所有这些来计算向量的长度,这个函数可以是 constexpr
因为我正在 constexpr
结构中的所有可能。
constexpr inline Real length() const { return const_sqrt<Real>(lengthSquared());}
如果在非 constexpr
上下文中调用 lenght()
函数,它会在 运行-time 中简单地 运行,但我有一个更快运行-执行时间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
, as :
constexpr inline Real length() const
{
return std::is_constant_evaluated()
? fast_sqrt<Real>(lengthSquared())
: const_sqrt<Real>(lengthSquared());
}
这种方法的问题在于,如果 length()
的 return 值用于初始化一个 constexpr
变量,否则 需要 为 constexpr
.
这在 lengthSquared()
的值在编译时已知的情况下是次优的(因此可以使用 const_sqrt
),但是 return length()
的值不需要是 constexpr
。然后 is_constant_evaluated
将 return 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
.
上下文
我正在制作一个数学库,我需要使用 sqrt()
。
由于 sqrt()
不是 constexpr
函数,我已经实现了 sqrt()
的 constexpr
版本,并且比使用汇编的 std::sqrt()
更快,因此必须在运行-时间。
现在,我正在使用所有这些来计算向量的长度,这个函数可以是 constexpr
因为我正在 constexpr
结构中的所有可能。
constexpr inline Real length() const { return const_sqrt<Real>(lengthSquared());}
如果在非 constexpr
上下文中调用 lenght()
函数,它会在 运行-time 中简单地 运行,但我有一个更快运行-执行时间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
, as
constexpr inline Real length() const
{
return std::is_constant_evaluated()
? fast_sqrt<Real>(lengthSquared())
: const_sqrt<Real>(lengthSquared());
}
这种方法的问题在于,如果 length()
的 return 值用于初始化一个 constexpr
变量,否则 需要 为 constexpr
.
这在 lengthSquared()
的值在编译时已知的情况下是次优的(因此可以使用 const_sqrt
),但是 return length()
的值不需要是 constexpr
。然后 is_constant_evaluated
将 return 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
.