简体   繁体   中英

Can C++ constexpr function actually accept non-constant expression as argument?

I have defined a constexpr function as following:

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

And this is what in the main function:

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

The program was compiled under OS X 10.8 with command clang++. I was surprised that the compiler did not produce any error message about foo(i) not being a constant expression, and the compiled program actually worked fine. Why?

The definition of constexpr functions in C++ is such that the function is guaranteed to be able to produce a constant expression when called such that only constant expressions are used in the evaluation. Whether the evaluation happens during compile-time or at run-time if the result isn't use in a constexpr isn't specified, though (see also this answer ). When passing non-constant expressions to a constexpr you may not get a constant expression.

Your above code should, however, not compile because i is not a constant expression which is clearly used by foo() to produce a result and it is then used as an array dimension. It seems clang implements C-style variable length arrays as it produces the following warning for me:

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

A better test to see if something is, indeed, a constant expression is to use it to initialize the value of a constexpr , eg:

constexpr int j = foo(i);

I used the code at the top (with "using namespace std;" added in) and had no errors when compiling using "g++ -std=c++11 code.cc" (see below for a references that qualifies this code) Here is the code and output:

 #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 

Now consider reference https://msdn.microsoft.com/en-us/library/dn956974.aspx It states: "...A constexpr function is one whose return value can be computed at compile when consuming code requires it. A constexpr function must accept and return only literal types. When its arguments are constexpr values, and consuming code requires the return value at compile time, for example to initialize a constexpr variable or provide a non-type template argument, it produces a compile-time constant. When called with non-constexpr arguments, or when its value is not required at compile-time, it produces a value at run time like a regular function. ( This dual behavior saves you from having to write constexpr and non-constexpr versions of the same function. )"
It gives as valid example:

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

This is an old question, but it's the first result on a google search for the VS error message "constexpr function return is non-constant". And while it doesn't help my situation, I thought I'd put my two cents in...

While Dietmar gives a good explanation of constexpr, and although the error should be caught straight away (as it is with the -pedantic flag) - this code looks like its suffering from some compiler optimization.

The value i is being set to 2, and for the duration of the program i never changes. The compiler probably noticed this and optimized the variable to be a constant (just replacing all references to variable i to the constant 2... before applying that parameter to the function), thus creating a constexpr call to foo().

I bet if you looked at the disassembly you'd see that calls to foo(i) were replaced with the constant value 4 - since that is the only possible return value for a call to this function during execution of the program.

Using the -pedantic flag forces the compiler to analyze the program from the strictest point of view (probably done before any optimizations) and thus catches the error.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM