使用constexpr
,可以在编译时或运行时根据参数计算函数。但通常,算法必须在编译时和运行时之间有所不同。例如。考虑constexpr版本的factorial。
constexpr int fact(int n)
{
return (n)?n*fact(n-1):1;
}
如果n
在运行时发生,那么该函数的效率是否低于一个forloop?是否有一些模板魔术来确定函数是在编译时还是在运行时执行并使用不同的算法?
更新:
factorial只是一个例子。如果没有constexpr
限制编码,所有constexpr
函数是否都有效?
例如:
constexpr int combinations(int n, int k)
{
//Assume all error conditions and edge conditions are taken care with ternary operator ?:
return fact(n)/(fact(k)*fact(n-k);
}
如果函数是在运行时编写的,它可以从Memoization中受益。即使这是可能的,我想很难表达这个函数,使它既可以constexpr
,也可以在运行时尽可能高效。
答案 0 :(得分:3)
不,据我所知,您无法检测编译器在给定调用中如何使用该函数,或者指示编译器根据constness使用不同的实现。
但首先,constexpr
函数仅限于单个return
语句,这意味着编译器可以(通常)轻松应用尾递归优化,将递归调用转换为循环。因此,这个问题是关于如何做过早优化,这不是一个好主意。低级优化是编译器的工作:让它。
其次,如果你真的想要编译器的工作,那么你可以命名函数,而不是试图将两个不同的函数实现无意义地塞进一个函数中。这个目的是什么?只有默默无闻。
对于给出的特定示例,
constexpr int fact(int n)
{
return (n)?n*fact(n-1):1;
}
编译器必须认识到它可以被重写为tail-recursive。我回忆起我之前关于它的SO问题的测试,即便是Visual C ++编译器也是如此。虽然由于一些莫名其妙的原因(可能与原始的x86处理器设计有关),但它使用了浮点类型:相同的高级逻辑,不同的低级结果。
作为一个稍微不那么激烈的帮助 - 编译器工作的努力,在测量并发现此功能使您的应用程序速度慢得令人无法接受,并且在检查了机器代码并发现编译器无法识别之后函数的尾递归,你可以按如下方式重写:
constexpr int fact( int multiplier, int n )
{
return (n != 0? fact( multiplier*n, n-1 ) : multiplier);
}
constexpr int fact( int n )
{
return fact( 1, n );
}
免责声明:编译器的脏手没有触及代码。