How to do a static assert that a pointer cast is trivial?
Let's say I have these types:
struct A {
int a;
};开发者_如何学运维
struct B {
int b;
};
struct C : public A, public B {
int c;
};
A C*
pointer can be cast to A*
pointer without adjusting the actual address at all. But when C*
is cast to B*
, the value must change. I'd like to ensure that two related types I have can be cast to each other without a change in address (i.e. that there is no multiple inheritance, or that the base class is the first base of the derived class). This could be checked at run-time, e.g. like so
assert(size_t(static_cast<A*>((C*)0xF000) == 0xF000);
assert(size_t(static_cast<B*>((C*)0xF000) != 0xF000);
That works. But this information is known at compile time, so I'm looking for a way to do a compile-time assert on it. The obvious ways of converting the above to a static assert (e.g. replace assert
with BOOST_STATIC_ASSERT
give the error "a cast to a type other than an integral or enumeration type cannot appear in a constant-expression" with g++ 4.2.
Portability isn't too important. Using gcc extensions, or hacky template tricks would all be fine.
Update: Found that almost the same question has been asked before: C++, statically detect base classes with differing addresses?. Using offsetof()
is the only useful suggestion there too.
"I'd like to ensure that two related types can be cast to each other without a change in address (i.e. that there is no multiple inheritance, or that the base class is the first base of the derived class)."
Your "i.e." isn't correct. For instance, it's entirely possible that the first 4 bytes of Derived
are a vtable pointer, even when Base
isn't polymorphic. Yes, C++ compilers are easier if (1) the first base subobject is at offset 0, and (2) the vtable pointer is at offset 0. But those two goals are inherently at odds, and there is no clear better choice.
Now, the first part could be tested in theory. With standard-layout types, there would be no difference in offset_of(Base, first_member)
and offset_of(Derived, first_member)
. But in practice, offset_of
doesn't work for interesting types; it's UB. And the whole point of this check is to check a type, so it should fail reliably for nonstandard-layout types.
Based on a suggestion from MSalters, and an answer from C++, statically detect base classes with differing addresses?, here is the closest thing to an answer I can come up with. It's probably gcc-specific, and requires knowing some member of the base class:
#pragma GCC diagnostic ignored "-Winvalid-offsetof" // To suppress warning.
BOOST_STATIC_ASSERT(offsetof(C, a) == offsetof(A, a));
BOOST_STATIC_ASSERT(offsetof(C, b) != offsetof(B, b));
#pragma GCC diagnostic warn "-Winvalid-offsetof"
Obviously this is both inconvenient and scary (requires to know a member and to turn off a warning).
精彩评论