Reputation: 4296
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
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