Namespaces
Variants
Views
Actions

Empty base optimization

From cppreference.com
< cpp‎ | language
Revision as of 10:32, 13 December 2013 by Cubbi (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
 
 
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements
Jump statements
Functions
function declaration
lambda function declaration
function template
inline specifier
exception specifications (deprecated)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
decltype specifier (C++11)
Specifiers
cv specifiers
storage duration specifiers
constexpr specifier (C++11)
auto specifier (C++11)
alignas specifier (C++11)
Initialization
Literals
Expressions
alternative representations
Utilities
Types
typedef declaration
type alias declaration (C++11)
attributes (C++11)
Casts
implicit conversions
const_cast conversion
static_cast conversion
dynamic_cast conversion
reinterpret_cast conversion
C-style and functional cast
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
class template
function template
template specialization
parameter packs (C++11)
Miscellaneous
Inline assembly
 

Allows the size of an empty base subobject to be zero.

[edit] Explanation

The size of any object or member subobject is required to be at least 1 even if the type is an empty class type (that is, a class or struct that has no non-static data members), in order to be able to guarantee that the addresses of distinct objects of the same type are always distinct.

However, base class subobjects are not so constrained, and can be completely optimized out from the object layout:

#include <cassert>
 
struct Base {}; // empty class
 
struct Derived1 : Base {
    int i;
};
 
int main()
{
    // the size of any object of empty class type is at least 1
    assert(sizeof(Base) == 1);
 
    // empty base optimization applies
    assert(sizeof(Derived1) == sizeof(int));
}

Empty base optimization is prohibited if one of the empty base classes is also the type of the first non-static data member, or the base of the type of the first non-static data member since the two base subobjects have the same type, and therefore are required to have different addresses within the object representation of the most derived type.

A typical example of such situation is the naive implementation of std::reverse_iterator (derived from the empty base std::iterator), which holds the underlying iterator (also derived from std::iterator) as its first non-static data member.

#include <cassert>
 
struct Base {}; // empty class
 
struct Derived1 : Base {
    int i;
};
 
struct Derived2 : Base {
    Base c; // Base, occupies 1 byte, followed by padding for i
    int i;
};
 
struct Derived3 : Base {
    Derived1 c; // derived from Base, occupies sizeof(int) bytes
    int i;
};
 
int main()
{
    // empty base optimization does not apply,
    // base occupies 1 byte, Base member occupies 1 byte
    // followed by 2 bytes of padding to satisfy int alignment requirements
    assert(sizeof(Derived2) == 2*sizeof(int));
 
    // empty base optimization does not apply,
    // base takes up at least 1 byte plus the padding
    // to satisfy alignment requirement of the first member (whose
    // alignment is the same as int)
    assert(sizeof(Derived3) == 3*sizeof(int));
}

Empty base optimization is required for StandardLayoutTypes in order to maintain the requirement that the pointer to a standard-layout object, converted using reinterpret_cast, points to its initial member, which is why one of the requirements for a standard layout type is "has no base classes with non-static data members and has no base classes of the same type as the first non-static data member".

(since C++11)

[edit] Notes

Empty base optimization is commonly used by allocator-aware standard library classes (std::vector, std::function, std::shared_ptr, etc) to avoid occupying any additional storage for its allocator member if the allocator is stateless. This is achieved by storing one of the required data members (e.g., begin, end, or capacity pointer for the vector) in an equivalent of boost::compressed_pair with the allocator.


[edit] References

  • C++11 standard (ISO/IEC 14882:2011):
  • 5.10 Equality operators [expr.eq](p: 2)
  • 5.3.3 Sizeof [expr.sizeof](p: 2)
  • 9 Classes [class](p: 4,7)
  • 9.2 Class members [class.mem](p: 20)
  • C++98 standard (ISO/IEC 14882:1998):
  • 5.10 Equality operators [expr.eq](p: 2)
  • 5.3.3 Sizeof [expr.sizeof](p: 2)
  • 9 Classes [class](p: 3)