C ++ constexpr函数实际上可以接受非常量表达式作为参数吗?

时间:2013-12-23 16:17:20

标签: c++ function c++11 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;
}

该程序是在OS X 10.8下使用命令clang ++编译的。我很惊讶编译器没有产生关于foo(i)不是常量表达式的任何错误消息,并且编译的程序实际上工作正常。为什么呢?

3 个答案:

答案 0 :(得分:21)

C ++中constexpr函数的定义使得函数保证能够在调用时生成常量表达式,以便在评估中仅使用常量表达式。但是,如果未在constexpr中使用结果,则评估是在编译时还是在运行时进行(请参阅this answer)。将非常量表达式传递给constexpr时,可能无法获得常量表达式。

但是,您的上述代码不应该编译,因为i不是foo()明确用于生成结果的常量表达式,然后将其用作数组维度。似乎clang实现了C风格的可变长度数组,因为它为我产生了以下警告:

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

更好的测试,看看某些东西是否确实是一个常量表达式,是用它来初始化constexpr的值,例如:

constexpr int j = foo(i);

答案 1 :(得分:1)

我使用了顶部的代码(添加了“using namespace std;”)并且在使用“g ++ -std = c ++ 11 code.cc”进行编译时没有错误(请参阅下面的参考资料,以确定是否符合此条件)代码)这是代码和输出:

 #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和non-constexpr版本。 )“
它给出了有效的例子:

   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;
   }

答案 2 :(得分:0)

这是一个老问题,但它是谷歌搜索VS错误消息的第一个结果&#34; constexpr函数返回是非常数&#34;。虽然它对我的情况没有帮助,但我以为我把我的两分钱放进......

尽管Dietmar给出了constexpr的一个很好的解释,虽然错误应该立即被捕获(就像-pedantic标志一样) - 这段代码看起来像是在进行一些编译器优化。

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

我敢打赌,如果你查看反汇编,你会看到对foo(i)的调用被替换为常量值4 - 因为这是在执行该函数期间调用此函数的唯一可能的返回值。程序

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