BobMorane
BobMorane

Reputation: 4296

constexpr constructor which may never be constexpr instantiated

I have come across a case of constexpr usage where I don't understand why the code compiles. Here is a minimal and reproducible example (Godbolt):

struct A
{
    A(int a_)
    :m_a{a_}
    {
    }

    int m_a;
};

// Compilation fails with this structure:
struct B
{
    constexpr B(A a_)
    :m_a{a_}
    {
    }

    A m_a;
};

// Compilation OK here, even if A is not constexpr.
struct BOK
{
    constexpr BOK(const A& a_)
    :m_a{a_}
    {
    }

    A m_a;
};

int main()
{
    // constexpr A a{2};          // Fails. OK, A's constructor is not constexpr
    // constexpr B b{A(2)};       // Fails. OK, A's constructor is not constexpr
    // constexpr BOK bok{A(2)};   // Fails. OK, A's constructor is not constexpr

    const BOK bok{A(2)};

    return 0;
}

I understand that B does not compile and the error is clear:

error: invalid type for parameter 1 of 'constexpr' function 'constexpr B::B(A)'

What I don't understand if why BOK does compile (especially since B doesn't). It seems the const& somehow makes it OK for the compiler. As I am showing in the main function, trying to instantiate a constexpr version of BOK fails to compile with error:

error: the type 'const BOK' of 'constexpr' variable 'bok' is not literal

as expected, but the struct's definition seems to be fine. It is weird because is seems the compiler thinks there might be a possibility for this to be constexpr... Is there? Why does this work?

I have tried this with both g++ and clang with the -std=c++20 switch.

Upvotes: 2

Views: 334

Answers (1)

Artyer
Artyer

Reputation: 40791

If you look solely the declaration of B's constructor:

constexpr B::B(A a_);

The compiler knows that this can not possibly be constexpr, since it would have to copy the non-literal type A.

If you look at BOK's constructor:

constexpr BOK::BOK(const A& a_);

This could be called in a constant expression (Like extern A value; BOK{value}), if you don't look at the actual definition.

Until C++23, it is "ill-formed, no diagnostic required" if there is no way to call a constexpr function or constructor in a constant expression. constexpr B::B(A a_) can be ruled out fast, and that's all that GCC is willing to check (it's a trade off for slower compile times vs more correctness). Your BOK struct's constructor is also ill-formed, but the compiler just isn't warning you.

As a side note, you can make A a literal type with any constexpr constructor:

#include <bit>

struct A
{
    A(int a_)
    :m_a{a_}
    {
    }

    int m_a;

    constexpr A() = delete;  // Now a literal type
};

struct B
{
    constexpr B(A a_)
    :m_a{a_}
    {
    }
    A m_a;
};

struct BOK
{
    constexpr BOK(const A& a_)
    :m_a{a_}
    {
    }

    A m_a;
};

constexpr A a{std::bit_cast<A>(2)};
constexpr B b{std::bit_cast<A>(2)};
constexpr BOK bok{std::bit_cast<A>(2)};

Upvotes: 3

Related Questions