The following code compiles on all the compilers that I tested so far:
struct Alignment {
struct Center {
constexpr Center() {}
};
static const Center CENTER;
};
constexpr int foo(Alignment::Center) {
return 42;
}
constexpr int x = foo(Alignment::CENTER);
Note that Alignment::CENTER
isn't constexpr
here. It doesn't even have a definition, only a declaration. Yet I'm using it as an argument to foo
to compute the constexpr
variable x
.
As soon as I add a member to Alignment::Center
, it fails to compile:
struct Center {
int n;
constexpr Center(): n(0) {}
};
Is the first example actually valid according to the C++ standard? What exactly are the rules here? Does the standard guarantee that empty structs are always constant expressions?
EDIT:
As this answer made me realize, adding a constexpr
copy constructor to Center
actually makes the second example compile, so it has nothing to do with Center
being empty:
struct Center {
int n;
constexpr Center(): n(0) {}
constexpr Center(const Center&): n(0) {}
};
The defaulted copy constructor copies every (direct) base class and (direct) data member. So for your class it is similar to:
constexpr Center::Center(const Center& other) noexcept {}
There are no members or bases to copy. Since other
isn't actually read, it doesn't need to have a constant value.
And the lvalue Alignment::CENTER
is a constant expression (i.e, you can store its address in a constexpr
variable), so it is perfectly fine to use as a reference in constant expressions. As long as you don't try to actually read it like std::bit_cast<char>(Alignment::CENTER)
.
It's similar to how this works:
extern std::vector<int> v;
constexpr int f(bool read, std::vector<int>& v) {
return read ? v.size() : 4;
}
static_assert(f(false, v) == 4); // v is not read, so it is a c
// static_assert(f(true, v)); // Not a constant expression, v is read