constexpr求值的分支/constexpr的重载

Branching on constexpr evaluation / overloading on constexpr

本文关键字:constexpr 重载 分支      更新时间:2023-10-16

设置:

我有一个使用SIMD内部函数的函数,并希望在一些constexpr函数中使用它。

为此,我需要将其设为constexpr。但是,SIMD内部函数没有标记为constexpr,编译器的常量计算器无法处理它们。

我尝试用做同样事情的C++constexpr实现来替换SIMD内部函数。这个函数在运行时慢了3.5倍,但我可以在编译时使用它(是吗?)。

问题

如何在常量表达式中使用此函数而不降低程序运行时的速度?

一些想法:

  • 为编译器常量表达式计算器添加对所有SIMD内部函数进行常量求值的支持,对于所有编译器来说:这可能是正确的解决方案,但却是一项不可能完成的艰巨任务

更务实的解决方案是:

  • 重载一个函数,这取决于它是否在常量表达式中执行(即,提供constexpr和非constexpr版本)
  • 或者,以某种方式在constexpr和运行时实现之间的constexpr函数内部进行分支(即,在分支中检测该函数是否在常量表达式内部执行)

无论如何,我愿意接受任何能解决我问题的建议。

提示

  • @RMartinhoFernandes在Lounge中建议使用__builtin_constant_p来检测函数参数是否都是常量表达式,在这种情况下,编译器有望至少在编译时尝试评估函数

失败的尝试

  • @Jarod42直接建议只使用两个独立的函数。我想简单地指出为什么这不起作用,因为这不是小事。这个解决方案假设在调用站点上,函数是否会被constexpr求值是已知的。但事实并非如此。考虑一个调用我的constexpr函数,它应该选择我的函数的哪个版本?它必须选择constexpr函数才能编译,但"外部"constexpr功能仍然可以在运行时求值。在这种情况下,它将使用"慢速"编译时实现,因此,这种方法不能解决问题

我会像这个一样做

constexpr int doit(int input, bool inconst = false) {
return inconst ? doitconsty(input) : doitfast(input);
}

如果对doit的调用在constexpr函数内部,该函数可以在运行时或编译时被调用以执行某些操作,那么只需转发标志

constexpr int f(int n, bool inconst = false) {
/* ... */
int importantInt = doit(n / 42, inconst);
/* ... */
return magicResult;
}

如果我没有记错的话,任何constexpr评估都有它的起点。将inconst传递到

enum foo { bar = f(256, true) }

如果您在运行时世界中,只需像调用其他一样调用f

int main() { std::cout << "test-case: " << f(256); }

需要注意的是,这对运算符不起作用,因为您不能在那里添加布尔参数。相反,如果可以的话,您可以用一些不同的方式传递值(对于像intbool这样的基元值,我们也不能重载运算符)。

template<typename T>
struct maybe_const_value {
T t;
bool isconst;
};
enum foo { bar = maybe_const_value{256, true} % magicTransform }; 
int main() { return maybe_const_value{265} % magicTransform; }

然后,运算符函数可以检查input.isconst并使用input.t作为实际值。