Can `consteval` delegating constructor be called from an ordinary constructor?
Asked Answered
L

2

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?

Latoria answered 12/1, 2022 at 7:36 Comment(8)
Without the member variable i GCC is happy too, fwiw. Even making i static seems to make GCC happy.Exhalation
As far as I understand it any consteval function will not result in a runtime callable function. And the compilers that reject the code are correct.Prevision
Mmm.. but why can't the compiler generate a run-time available constructor A(int) that uses the compile-time known constructor A()? Plus, see my comment above.Exhalation
You can also appease GCC by removing the default member initializer. As it stands, this is asking an immediate function to fiddle with a run-time entity.Fair
@StoryTeller-UnslanderMonica, but why would the presence of a member variable such as i influence this, given it is not even used by either ctor?Exhalation
@Exhalation - Of course it's used. The c'tor does the actual initialization.Fair
Hmm, still getting my head around this I still think clang is a bit too lenient : gcc.godbolt.org/z/5v7Pjo7G4Prevision
@StoryTeller-UnslanderMonica, true, silly me.Exhalation
S
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.

Shod answered 17/2, 2022 at 11:3 Comment(1)
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: gcc.godbolt.org/z/v8Ta3zYKrLatoria
P
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() { }
      |                      ^
Presbyopia answered 12/1, 2022 at 8:58 Comment(2)
Can you make this a constant expression similar to op1 and op2 - by declaring constexpr A a {}?Wimble
@Wimble Yes, it's possible. But it won't solve the OP's issue. The problem with this will still be there.Presbyopia

© 2022 - 2024 — McMap. All rights reserved.