Re: Empty member optimization

David Abrahams wrote:
I think the question is whether it's ever desirable to break the current C++ invariant that no two objects of the same type will ever share an address **in generic code** -- that is, when you don't know anything about the assumptions that may be made by that type.
An alternative implementation is to privately inherit from the specified type for empty classes as compressed_pair does. This maintains the invariant but has the drawback that inheritance is visible in user code. For a generic compressed_tuple the private inheritance should not be a problem. João

Joao Abecasis <jpabecasis@zmail.pt> writes:
David Abrahams wrote:
I think the question is whether it's ever desirable to break the current C++ invariant that no two objects of the same type will ever share an address **in generic code** -- that is, when you don't know anything about the assumptions that may be made by that type.
An alternative implementation is to privately inherit from the specified type for empty classes as compressed_pair does. This maintains the invariant but has the drawback that inheritance is visible in user code.
Yeah, the primary danger being unintentional overriding of virtual functions from the private base. I like the protection your idea provides. You could arrange a compressed_pair based on this design that still maintains the invariant. And then you could make it smart enough to aggregate other compressed_pairs without ever allocating two of the same type at the same address.
For a generic compressed_tuple the private inheritance should not be a problem.
I'm not sure what you mean by that, sorry. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Joao Abecasis <jpabecasis@zmail.pt> writes:
David Abrahams wrote:
I think the question is whether it's ever desirable to break the current C++ invariant that no two objects of the same type will ever share an address **in generic code** -- that is, when you don't know anything about the assumptions that may be made by that type.
An alternative implementation is to privately inherit from the specified type for empty classes as compressed_pair does. This maintains the invariant but has the drawback that inheritance is visible in user code.
Yeah, the primary danger being unintentional overriding of virtual functions from the private base. I like the protection your idea provides. You could arrange a compressed_pair based on this design that still maintains the invariant. And then you could make it smart enough to aggregate other compressed_pairs without ever allocating two of the same type at the same address.
I think virtual classes are not an issue because they are not empty. There's always a pointer to vtable or something. Both compressed_member and compressed pair store non-empty types as regular variables and those keep the invariant at all times. The invariant is violated only with a mix of empty compressed_members and empty member variables.
For a generic compressed_tuple the private inheritance should not be a problem.
I'm not sure what you mean by that, sorry.
The motivation for writing compressed_member was to bring EBO to member variables in user code. Using private inheritance brings about some quirks, e.g. : struct A {}; struct B : compressed_member<A> { int something() { A a; } }; is illegal with private inheritance. It also interferes with overload resolution and is_base_and_derived. We'd have a "member" variable that shows up as a base everywhere. In a generic compressed_tuple library some of these are non-issues and the usefulness of compressed_pair stands for that. Regards, João

Joao Abecasis <jpabecasis@zmail.pt> writes:
David Abrahams wrote:
Joao Abecasis <jpabecasis@zmail.pt> writes:
David Abrahams wrote:
I think the question is whether it's ever desirable to break the current C++ invariant that no two objects of the same type will ever share an address **in generic code** -- that is, when you don't know anything about the assumptions that may be made by that type.
An alternative implementation is to privately inherit from the specified type for empty classes as compressed_pair does. This maintains the invariant but has the drawback that inheritance is visible in user code.
Yeah, the primary danger being unintentional overriding of virtual functions from the private base. I like the protection your idea provides. You could arrange a compressed_pair based on this design that still maintains the invariant. And then you could make it smart enough to aggregate other compressed_pairs without ever allocating two of the same type at the same address.
I think virtual classes
Err, polymorphic classes, please.
are not an issue because they are not empty. There's always a pointer to vtable or something.
Technically there does not have to be. An implementation could store the information in a map indexed on the address of the object. That said, no implementations do that. So I withdraw my concern.
Both compressed_member and compressed pair store non-empty types as regular variables
Ahem; data members. ;-)
and those keep the invariant at all times.
Right.
The invariant is violated only with a mix of empty compressed_members and empty member variables.
Two empty compressed_members used as bases should be sufficient. struct X : compressed_member<Base> , compressed_member<Derived> {};
For a generic compressed_tuple the private inheritance should not be a problem.
I'm not sure what you mean by that, sorry.
The motivation for writing compressed_member was to bring EBO to member variables in user code. Using private inheritance brings about some quirks, e.g. :
struct A {};
struct B : compressed_member<A> { int something() { A a; } };
is illegal with private inheritance.
??? why would private inheritance have an effect on the legality of auto variables of any type??
It also interferes with overload resolution and is_base_and_derived. We'd have a "member" variable that shows up as a base everywhere.
I agree that's a problem.
In a generic compressed_tuple library some of these are non-issues and the usefulness of compressed_pair stands for that.
I see. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote: [snip lots of embarassing comments :-) ]
The invariant is violated only with a mix of empty compressed_members and empty member variables.
Two empty compressed_members used as bases should be sufficient.
struct X : compressed_member<Base> , compressed_member<Derived> {};
Hmm... I completely missed those cases.
struct A {};
struct B : compressed_member<A> { int something() { A a; } };
is illegal with private inheritance.
??? why would private inheritance have an effect on the legality of auto variables of any type??
In the above example, A refers to a private inaccessible base of B. The scoping operator has to be used to instantiate A inside B. I'll have a new implementation later in the week. I'll have to drop the memberwise inheritance and head for something similar to a tuple. Possibly following the syntax suggestion of Joaquín López Muñoz. In the new implementation a structure is built to actually hold the data (either through public inheritance or as data members ;-) but it is never instantiated. The compiler ensures the invariants are maintained for that structure. Then I take its size and pad my compressed_tuple-wannabe with it. The same casting hacks, placement new, etc, are then used to avoid inheritance, provide a clean interface and maintain the initialization order. Only this new tuple will not be empty ever. And we'll have to live with that ;-). I took a look at Borland 5.5.1 and was able to come up with a few tricks that provide some form of EBO. But it's a no win situation because there is no way to actually determine it a given type is empty (with this compiler) to start with. For instance, empty classes take up the size of the base class. If the (final) base class is empty it's size is 8 but if it has a single char data member it's size will be 1! Regards, João Abecasis

Joao Abecasis wrote:
[...] I took a look at Borland 5.5.1 and was able to come up with a few tricks that provide some form of EBO. But it's a no win situation because there is no way to actually determine it a given type is empty (with this compiler) to start with. For instance, empty classes take up the size of the base class. If the (final) base class is empty it's size is 8 but if it has a single char data member it's size will be 1!
You need to set structure packing, probably. Something like this: #include <pshpack1.hpp> // Your code here #include <poppack1.hpp> Also set alignment for WORD boundaries rather than DWORD. Dave
participants (3)
-
David Abrahams
-
David B. Held
-
Joao Abecasis