10

In the following struct definition, the constructor A(int) delegates its work to immediate-function constructor A():

struct A {       
    int i = 0;
    consteval A() = default;
    A(int) : A() {}
};

Clang accepts it, but not GCC complaining:

error: 'this' is not a constant expression

and MSVC:

'A::A': call to immediate function is not a constant expression

Demo: https://gcc.godbolt.org/z/7e3fWzYzr

Which compiler is correct?

Fedor
  • 17,146
  • 13
  • 40
  • 131

2 Answers2

1

I think such non-constexpr constructor should be accepted, as gcc has already been accepting the following code.

struct Foo {
    int a = 0;
    
    consteval Foo() = default;
    
    static consteval Foo make_foo() noexcept { return Foo{}; }
    
    // Foo(iny) : Foo() {}
    Foo(int) : Foo(make_foo()) {}
};

int main()
{
    static_cast<void>(Foo{42});
}

I guess this is also a bug in the C++ standard, seems tracked by the issue CWG2410.

IMO currently consteval constructor calls are not specified correctly, because in C++ a constructor call is never itself an expression, and hence shouldn't be considered as a constant expression.

F.v.S.
  • 167
  • 1
  • 8
  • 2
    Thanks. To make your example closer to the one in the question, one can write `A(int) : A(A()) {}`, which is indeed accepted by GCC: https://gcc.godbolt.org/z/v8Ta3zYKr – Fedor Feb 18 '22 at 10:17
0

As you know, this is a parameter to all non-static member functions. And parameters are never constant expressions. So this can't be used in a constant expression.

When you initialize the non-static member variable like:

int i = 0;

this initialization will take place at run-time. And don't think that your empty (defaulted) constructor is doing nothing. It actually will have to initialize i with zero at run-time. But how is the compiler supposed to generate a consteval function (like your default ctor) which does run-time tasks?! It's just a contradiction.

Let me show you a similar scenario:

consteval int multiply( const int op1, const int op2 )
{
    return op1 * op2;
}

int main( )
{
    constexpr int op1 { 2 };
    constexpr int op2 { 3 };
    std::cout << multiply( op1, op2 ) << '\n'; // compiles for obvious reasons

    int op3 { 2 };
    int op4 { 3 };
    std::cout << multiply( op3, op4 ) << '\n'; // doesn't compile
}

And:

test.cpp: In function 'int main()':
test.cpp:57:32: error: the value of 'op3' is not usable in a constant expression
   57 |         std::cout << multiply( op3, op4 ) << '\n';
      |                                ^~~
test.cpp:55:13: note: 'int op3' is not const
   55 |         int op3 { 2 };
      |             ^~~

In the 1st call, the consteval function is receiving constexpr arguments. So it's happy.
In the 2nd call, it's receiving non-constexpr arguments.
this for your consteval ctor causes a similiar condition:

test.cpp: In constructor 'A::A(int)':
test.cpp:41:22: error: 'this' is not a constant expression
   41 |         A( int ) : A() { }
      |                      ^
digito_evo
  • 3,216
  • 2
  • 14
  • 42
  • 1
    Can you make `this` a constant expression similar to `op1` and `op2` - by declaring `constexpr A a {}`? – Sebastian Jan 12 '22 at 11:18
  • @Sebastian Yes, it's possible. But it won't solve the OP's issue. The problem with `this` will still be there. – digito_evo Jan 12 '22 at 12:14