Search code examples
c++gccc++11constexprside-effects

Why can I call a non-constexpr function inside a constexpr function?


Consider the following code:

#include <stdio.h>

constexpr int f()
{
    return printf("a side effect!\n");
}

int main()
{
    char a[f()];
    printf("%zd\n", sizeof a);
}

I would have expected the compiler to complain about the call to printf inside f, because f is supposed to be constexpr, but printf is not. Why does the program compile and print 15?


Solution

  • The program is ill-formed and requires no diagnostic according to the C++11 draft standard section 7.1.5 The constexpr specifier paragraph 5 which says:

    For a constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression (5.19), the program is ill-formed; no diagnostic required.

    and provides the following example:

    constexpr int f(bool b)
      { return b ? throw 0 : 0; } // OK
    constexpr int f() { return f(true); } // ill-formed, no diagnostic required
    

    and section 5.19 paragraph 2 says:

    A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [...]

    and includes:

    — an invocation of a function other than a constexpr constructor for a literal class or a constexpr function [ Note: Overload resolution (13.3) is applied as usual —end note ];

    We would probably prefer a diagnostic in this case, it could just be an oversight, I have a bug report for a similar situation where gcc does not produce an error but we would probably like it to: Is the compiler allowed leeway in what it considers undefined behavior in a constant expression?.

    Update

    Using the -fno-builtin flag will cause gcc to generate the following error:

     error: call to non-constexpr function 'int printf(const char*, ...)'
     return printf("a side effect!\n");
                                     ^
    

    So gcc does consider this ill-formed it is just ignores it when it is using the builtin version of printf.

    Although somewhat inconsistently using the -pedantic produces the following warning:

    warning: ISO C++ forbids variable length array 'a' [-Wvla]
     char a[f()];
               ^
    

    Note that using f() to initialized a constexpr variable:

    constexpr int x = f() ;
    

    does generate an error:

    error: 'printf(((const char*)"a side effect!\012"))' is not a constant expression
    

    Note that additionally in the more general case a compiler is not allowed mark standard library functions as constexpr unless explicitly allowed by the standard.