7

使用constexpr,可以根据参数在编译时或运行时评估函数。但通常,编译时和运行时的算法必须不同。例如。考虑阶乘的 constexpr 版本。

constexpr int fact(int n)
{
return (n)?n*fact(n-1):1;
}

如果n发生在运行时,该函数会不会比一个 forloop 效率低?是否有一些模板魔术来确定函数是在编译时执行还是在运行时执行并使用不同的算法?

更新
阶乘只是一个例子。如果不受限制地编码,所有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在运行时既高效又尽可能高效。

4

1 回答 1

3

不,据我所知,您无法检测到编译器如何在给定调用中使用该函数,或者指导编译器根据常量使用不同的实现。

但首先,constexpr函数仅限于单个return语句,这意味着编译器可以(通常)轻松应用尾递归优化,将递归调用变成循环。因此,这个问题是关于如何进行过早优化,这不是一个好主意。低级优化是编译器的工作:顺其自然。

其次,如果你真的想做编译器的工作,那么你可以只命名函数,而不是试图毫无意义地将两个不同的函数实现塞进一个。那是为了什么目的?只有默默无闻。


对于给出的特定示例,

constexpr int fact(int n)
{
    return (n)?n*fact(n-1):1;
}

编译器必须认识到它可以被重写为尾递归。正如我从我对早先关于它的 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 );
}

免责声明:编译器的脏手没有触及代码。

于 2013-01-18T11:07:26.697 回答