12

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?

Kijewski
  • 25,517
  • 12
  • 101
  • 143
God_of_Thunder
  • 753
  • 3
  • 20
  • 46

3 Answers3

23

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, e.g.:

constexpr int j = foo(i);
Community
  • 1
  • 1
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I received no warning message during compilation. Difference in compiler? – God_of_Thunder Dec 23 '13 at 16:35
  • 1
    @God_of_Thunder: most likely my somewhat anal compiler flags: `clang++ -stdlib=libc++ -nostdinc++ -std=c++11 -O2 -pedantic -W -Wall -c test.cpp` However, I like them this way as they capture lots of errors before even running. – Dietmar Kühl Dec 23 '13 at 16:37
  • 1
    I tried to add the above constexpr definition into my program and compile with my old clang++ command and the error message "read of non-const variable 'i' is not allowed in a constant expression" was produced. So the compiler extension only works for array dimension but not ordinary constexpr definition? – God_of_Thunder Dec 23 '13 at 16:42
  • 1
    @God_of_Thunder: Correct. Because the extension is not causing your function to produce a constant expression when passed a non-constant expression value. It is allowing a non-constant expression value (the one produced by your function) to be used for the size of an array. – Benjamin Lindley Dec 23 '13 at 16:44
  • @God_of_Thunder: the compiler extension allows non-constant expressions for the size of [stack-based] arrays. It doesn't have anything to do with constant expression, really, other than it lifting a specific restriction. – Dietmar Kühl Dec 23 '13 at 16:45
  • Would the program produce any runtime error or cause any other potential problem if non-constexpr is allowed to be used as array dimension due to this compiler extension? – God_of_Thunder Dec 23 '13 at 16:51
  • 1
    @God_of_Thunder: You'll need to research the specification for the compiler extension! I don't know. Probably the biggest threat is to use arrays which don't fit and which are bound to silently create stack overflows. There were discussions about adding this extension for C++14 but there were concerns which caused the extension to be delayed. I didn't pay to much attention to reasons for this extension to be delay, however (I don't think I'm interested in this particular extension). – Dietmar Kühl Dec 23 '13 at 17:01
  • I have tried to use your command above to compile my program and I got this error message: "'iostream' file not found". Did I miss something? – God_of_Thunder Dec 23 '13 at 17:44
  • @God_of_Thunder: well, I just posted what I'm using. I have my environment set up to use a different standard C++ library. You might need to tell the compiler where to find the standard library headers and library. – Dietmar Kühl Dec 23 '13 at 18:37
  • 1
    @God_of_Thunder: Only the `-pedantic -W -Wall` part is relevant to your case. I recommend using those, as well, to catch as many potential errors as possible, but you should always consult your compiler's documentation to see what each switch does before blindly applying them. – JohannesD Dec 23 '13 at 20:26
  • This was already discussed at length, see: http://isocpp.org/blog/2013/01/when-does-a-constexpr-function-get-evaluated-at-compile-time-stackoverflow. You should change/clear your statement about passing constexpr values as the single thing necessary for constexpr evaluation. Just to avoid confusion in people's mind. – oblitum Dec 31 '13 at 17:43
2

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

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.

random_acts
  • 369
  • 1
  • 4
  • 15
  • `-pedantic` doesnt change any optimization level, it just disables non-standard extensions. Dietmar's explanation seems better to me, although if you could produce assembly that proves your point then you would have a better case! – M.M May 15 '14 at 23:24