提问者:小点点

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


我正在制作一个数学库,需要使用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());
}

共2个答案

匿名用户

在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是没有意义的。