C++constexpr函数真的能接受非常量表达式作为参数吗



我定义了一个constexpr函数如下:

constexpr int foo(int i)
{
return i*2;
}

这就是主要功能中的内容:

int main()
{
int i = 2;
cout << foo(i) << endl;
int arr[foo(i)];
for (int j = 0; j < foo(i); j++)
arr[j] = j;
for (int j = 0; j < foo(i); j++)
cout << arr[j] << " ";
cout << endl;
return 0;
}

该程序是在OSX10.8下用clang++命令编译的。令我惊讶的是,编译器没有产生任何关于foo(I)不是常量表达式的错误消息,而且编译后的程序实际上运行得很好。为什么?

C++中constexpr函数的定义是这样的,即保证函数在调用时能够生成常量表达式,从而在求值中只使用常量表达式。但是,如果结果没有在constexpr中使用,则没有指定计算是在编译时进行还是在运行时进行(另请参阅此答案)。将非常量表达式传递给constexpr时,可能无法获得常量表达式。

但是,上面的代码不应该编译,因为i不是一个常量表达式,foo()显然使用它来生成结果,然后它被用作数组维度。clang似乎实现了C风格的可变长度数组,因为它给我带来了以下警告:

warning: variable length arrays are a C99 feature [-Wvla-extension]

一个更好的测试是用它初始化constexpr的值,例如:

constexpr int j = foo(i);

我使用了顶部的代码(添加了"using namespace std

#include <iostream>
using namespace std;
constexpr int foo(int i)
{
return i*2;
}
int main()
{
int i = 2;
cout << foo(i) << endl;
int arr[foo(i)];
for (int j = 0; j < foo(i); j++)
arr[j] = j;
for (int j = 0; j < foo(i); j++)
cout << arr[j] << " ";
cout << endl;
return 0;
}
output:
4
0 1 2 3 

现在考虑参考https://msdn.microsoft.com/en-us/library/dn956974.aspx它指出:"…constexpr函数是一个返回值可以在编译时计算的函数,当使用代码需要它时。constexpr函数必须只接受和返回文字类型。当它的参数是constexpr值,并且消费代码在编译时需要返回值时,例如初始化constexpr变量或提供非类型模板参数,它会生成编译时常量。当使用非constexpr参数调用时,或者在编译时不需要它的值时,它会在运行时像正则函数一样生成一个值。(这种双重行为使您不必编写同一函数的constexpr和nonconstexpr版本。)">
它给出了一个有效的例子:

constexpr float exp(float x, int n)
{
return n == 0 ? 1 :
n % 2 == 0 ? exp(x * x, n / 2) :
exp(x * x, (n - 1) / 2) * x;
}

这是一个老问题,但这是在谷歌上搜索VS错误消息"constexpr函数返回非常量"的第一个结果。虽然这对我的处境没有帮助,但我想我应该把我的两分钱投入…

虽然Dietmar对constexpr给出了很好的解释,尽管错误应该立即被捕获(就像它带有-pedantic标志一样),但这段代码看起来像是受到了一些编译器优化的影响。

值i被设置为2,并且在程序的持续时间内,i从未改变。编译器可能注意到了这一点,并将变量优化为常量(只是在将该参数应用于函数之前,将变量i的所有引用替换为常量2…),从而创建了对foo()的constexpr调用。

我敢打赌,如果你看一下反汇编,你会发现对foo(I)的调用被常量值4取代了——因为这是在程序执行期间对该函数调用的唯一可能的返回值。

使用-pedantic标志迫使编译器从最严格的角度分析程序(可能在任何优化之前完成),从而捕获错误。

最新更新