Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr (but not really) constructor compiles in gcc but not in clang

I was playing around with constexpr constructors in C++14 and above and noticed something strange. Here is my code:

#include <iostream>
#include <string>

using std::cout;
using std::endl;

#define PFN(x) cout << x << __PRETTY_FUNCTION__ << endl
#define PF     PFN("")
#define NL     cout << endl

struct A {
    constexpr A() { PF; }
    virtual ~A() { PF; NL; }
};

struct B : A {
    constexpr B() { PFN(" "); }
    virtual ~B() { PFN(" "); }
};

int main(int argc, char** argv) {
    { A a; }
    { B b; }
    A* a = new B;
    delete a;
    return 0;
}

Simple enough example. I compiled it with g++ -std=c++14 -o cx_test cx_test.cpp, expecting it to give me a compile error (because I am using cout and the stream operator to print the function's name. But, to my surprise, it compiled! When I ran it, it gave the following output:

$> g++ -std=c++14 -o cx_test cx_test.cpp && ./cx_test
constexpr A::A()
virtual A::~A()

constexpr A::A()
 constexpr B::B()
 virtual B::~B()
virtual A::~A()

constexpr A::A()
 constexpr B::B()
 virtual B::~B()
virtual A::~A()
$>

But, when I compile with clang, I get:

$> clang++ -std=c++14 -o cx_test cx_test.cpp && ./cx_test
cx_test.cpp:12:15: error: constexpr constructor never produces a constant expression [-Winvalid-constexpr]
    constexpr A() { PF; }
              ^
cx_test.cpp:12:21: note: non-constexpr function 'operator<<<std::char_traits<char> >' cannot be used in a constant expression
    constexpr A() { PF; }
                    ^
cx_test.cpp:9:12: note: expanded from macro 'PF'
#define PF PFN("")
           ^
cx_test.cpp:8:26: note: expanded from macro 'PFN'
#define PFN(x) cout << x << __PRETTY_FUNCTION__ << endl
                         ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/ostream:556:5: note: declared here
    operator<<(basic_ostream<char, _Traits>& __out, const char* __s)
    ^
1 error generated.
$>

This seems like a bug with g++ because the constructor appears to violate the restrictions of constexpr, but I am not quite sure. Which compiler is correct?

Here is the g++ version and here is the clang version (on ideone).

like image 359
callyalater Avatar asked Feb 22 '18 20:02

callyalater


People also ask

What are constexpr constructors in Python?

With constexpr constructors, objects of user-defined types can be included in valid constant expressions. The containing class must not have any virtual base classes. Each of the parameter types is a literal type. It is not a function try block.

Can a constexpr function contain a Cout statement?

Fair enough, constexpr functions can't contain any cout statements, as we know. But what happens if we do this? clang allows it! OK, but this can't possibly be a constexpr function even though it is marked constexpr, let's try to use it in constexpr context. It works! So it must be clang bug? Reason I say clang because gcc complains:

What are the requirements for a constexpr function in C++?

A constexpr function must satisfy the following requirements: for constructor and destructor (since C++20), the class must have no virtual base classes

What is the difference between constexpr and constexpr in C++ 14?

C++ 14 allows more than one statement. constexpr function should refer only to constant global variables. constexpr function can call only other constexpr function not simple function. The function should not be of a void type and some operators like prefix increment (++v) are not allowed in constexpr function.


1 Answers

Both gcc and clang are correct, your program is ill-formed no diagnostic required since there is no way to invoke the constructors such that they could be evaluated as a sub-expression of a core constant expression.

From [dcl.constexpr]p5:

For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression ([expr.const]), the program is ill-formed; no diagnostic required. [ Example:

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

struct B {
  constexpr B(int x) : i(0) { }             // x is unused
  int i;
};

int global;

struct D : B {
  constexpr D() : B(global) { }             // ill-formed, no diagnostic required
                                            // lvalue-to-rvalue conversion on non-constant global
};

— end example ]

If we force the constructor to be evaluated in a constant expression context then you will receive a diagnostic from gcc as well (see it live):

{ constexpr A a; }
like image 196
Shafik Yaghmour Avatar answered Sep 24 '22 21:09

Shafik Yaghmour