Skip to content
C++
Idiom
since C++98
Intermediate

Base From Member

Guarantee a member object is fully constructed before a dependent base class by promoting it into an earlier base class.

Base From Membersince C++98

A technique that resolves the base-before-member initialization constraint by promoting a data member into a dedicated private base class, ensuring it is fully constructed before any dependent base that requires it.

Overview

C++ imposes a fixed initialization order within a class: every base class subobject is fully constructed before any non-static data member, and bases are initialized in the order they appear in the base-class list β€” not the order written in the member-initializer list. This rule is immutable and cannot be worked around by rearranging the initializers.

The problem surfaces when a base class constructor requires a resource β€” a buffer, a policy object, a handle β€” that the derived class also needs to own. By the time the base is being initialized, the derived class's members do not yet exist.

cpp
// BROKEN β€” streambuf_ does not exist when basic_ios is constructed
class LogStream : public std::basic_ios<char> {
    std::stringbuf streambuf_;   // initialized AFTER basic_ios
public:
    LogStream()
        : std::basic_ios<char>(&streambuf_)  // UB: &streambuf_ is garbage
        , streambuf_{}
    {}
};

The Base From Member idiom resolves this by making the member a base class. Because bases are initialized in declaration order, declaring the carrier before the dependent base guarantees the resource exists when needed.

cpp
// Fixed β€” SbufBase is initialized first, basic_ios sees a valid pointer
struct SbufBase {
    std::stringbuf buf;
};

class LogStream
    : private SbufBase          // initialized 1st
    , public  std::basic_ios<char>  // initialized 2nd β€” buf is alive
{
public:
    LogStream()
        : SbufBase{}
        , std::basic_ios<char>(&buf)  // safe
    {}
};

The Standard Library itself is built on this pattern. Implementations of std::basic_stringstream must initialize a std::basic_stringbuf before the std::basic_ios subobject that stores a pointer to it. Every conforming implementation uses a carrier base, often named __str_buf_base or similar, declared ahead of the stream base in the hierarchy.

Syntax

The canonical shape is a two-level inheritance chain where the carrier comes first:

cpp
// Carrier β€” holds the resource, no other responsibilities
template<typename T>
struct BaseMember {
    explicit BaseMember(auto&&... args)
        : value(std::forward<decltype(args)>(args)...)  // C++11 perfect forward
    {}
    T value;
};

// Derived class β€” carrier precedes every base that depends on value
class MyClass
    : private BaseMember<Resource>  // initialized 1st
    , public  DependentBase         // initialized 2nd
{
public:
    explicit MyClass(Resource r)
        : BaseMember<Resource>(std::move(r))          // C++11 std::move
        , DependentBase(BaseMember<Resource>::value)  // safe: resource is alive
    {}
};

The carrier is almost always private: it is a construction artefact, not part of the public interface. Access from within the derived class uses the qualified name (BaseMember<T>::value) to avoid ambiguity if the derived class also defines a member with the same name.

Examples

Custom stream with an owned buffer

cpp
#include <ostream>
#include <streambuf>
#include <array>
#include <string_view>   // C++17

class FixedBuf : public std::streambuf {
    std::array<char, 4096> storage_;
public:
    FixedBuf() { setp(storage_.data(), storage_.data() + storage_.size()); }

    std::string_view view() const {   // C++17
        return {pbase(), static_cast<std::size_t>(pptr() - pbase())};
    }
};

// Carrier base
struct FixedBufBase {
    FixedBuf buf;
};

// FixedStream owns its buffer with no external lifetime dependency
class FixedStream
    : private FixedBufBase       // must precede ostream β€” base-from-member
    , public  std::ostream
{
public:
    FixedStream() : FixedBufBase{}, std::ostream(&buf) {}

    std::string_view view() const { return buf.view(); }  // C++17
};

int main() {
    FixedStream fs;
    fs << "status=" << 42 << " ok\n";
    auto s = fs.view();   // "status=42 ok\n"
}

File-backed logger β€” handle open before base hooks fire

Some base classes invoke virtual functions or register callbacks during their own constructor. If those callbacks need a valid resource, the carrier must guarantee the resource exists before the base constructor body runs.

cpp
#include <cstdio>
#include <stdexcept>

// Non-copyable, non-movable handle carrier
struct FileHandleBase {
    explicit FileHandleBase(const char* path)
        : fp(std::fopen(path, "w"))
    {
        if (!fp) throw std::runtime_error("cannot open log file");
    }
    ~FileHandleBase() { if (fp) std::fclose(fp); }

    FileHandleBase(const FileHandleBase&)            = delete;  // C++11
    FileHandleBase& operator=(const FileHandleBase&) = delete;  // C++11

    std::FILE* fp;
};

// LoggerBase registers itself on construction and calls open_hook()
struct LoggerBase {
    explicit LoggerBase(std::FILE* out);  // stores out, calls open_hook()
    virtual void open_hook() {}
    virtual ~LoggerBase() = default;      // C++11
};

class FileLogger
    : private FileHandleBase  // fp is valid before LoggerBase::LoggerBase runs
    , public  LoggerBase
{
public:
    explicit FileLogger(const char* path)
        : FileHandleBase(path)
        , LoggerBase(FileHandleBase::fp)  // safe
    {}
};

Multiple carriers of the same type

When two members share a type, two identically typed carrier bases create ambiguity. Use a discriminator to distinguish them:

cpp
template<typename T, int N = 0>
struct TaggedBase {
    T value;
};

class DualBufferStream
    : private TaggedBase<std::stringbuf, 0>  // input buffer
    , private TaggedBase<std::stringbuf, 1>  // output buffer
    , public  std::iostream
{
    using InBase  = TaggedBase<std::stringbuf, 0>;
    using OutBase = TaggedBase<std::stringbuf, 1>;
public:
    DualBufferStream()
        : InBase{}, OutBase{}
        , std::iostream(nullptr)
    {
        set_rdbuf(nullptr); // configure separately after construction
    }
};

Boost provides boost::base_from_member<T, N> as a production-ready version of this pattern, with the same discriminator semantics.

Best Practices

Name carriers after what they hold. StringBufBase or HandleBase is immediately navigable; MemberBase or Base1 collides with every other use of the idiom in the codebase.

Always declare the carrier private. It is an initialization artefact. Exposing it as a public or protected base leaks internal structure and may enable unintended slicing or casting.

Consider [[no_unique_address]] for stateless carriers (C++20). When the promoted member is a zero-size policy type, making it a base exploits the Empty Base Optimization but forces inheritance. C++20 [[no_unique_address]] achieves identical space savings as a member while preserving ordinary member-initialization order within a class β€” members initialize in declaration order, so a zero-size [[no_unique_address]] policy member declared before a dependent member works without inheritance.

cpp
// C++20 β€” no base class needed when the policy is stateless
struct EmptyPolicy {};

class Thing {
    [[no_unique_address]] EmptyPolicy policy_;  // C++20 β€” zero size
    DependentMember dep_;                       // initialized after policy_
public:
    Thing() : policy_{}, dep_{policy_} {}
};

For non-empty carriers, the inheritance-based form remains the correct solution.

Comment the ordering constraint. The declaration order in the base-class list is load-bearing, but nothing in the syntax signals this to a reader. A single inline comment prevents silent regressions from well-meaning reformatters.

cpp
class MyStream
    : private BufBase           // must precede ostream β€” base-from-member
    , public  std::ostream

Common Pitfalls

Wrong base order. Swapping the carrier and the dependent base compiles without diagnostic in most cases and silently produces undefined behaviour: the dependent base constructor receives a pointer or reference to an object that has not yet begun its lifetime. AddressSanitizer and UBSan catch this at runtime; no static warning is guaranteed.

Virtual bases break the guarantee. If the dependent base is also a virtual base, the most-derived class is responsible for initializing it, not the intermediate derived class. The carrier trick cannot push initialization earlier in that scenario; the design must be restructured.

Destructors consume the promoted member. Destruction proceeds in reverse construction order: the dependent base destructor runs before the carrier destructor. If the dependent base's destructor flushes to the buffer (as std::ostream::~basic_ostream does), the buffer is still alive β€” the ordering is correct. Verify this invariant holds whenever you modify the class hierarchy.

Unqualified member access. If the derived class declares a member with the same name as the carrier's field, unqualified references inside the class resolve to the derived member. Always use a qualified name (InBase::value) or a protected accessor to reference the promoted member.

boost::base_from_member<T, N> in Practice

Boost ships a production-ready carrier in <boost/utility/base_from_member.hpp>. The template takes the member type T and an integer discriminator N (defaults to 0); the wrapped value is accessible as the public field member. Using Boost eliminates the boilerplate of writing a new carrier struct for every type.

cpp
// C++11 β€” rewrite FixedStream using boost::base_from_member
#include <boost/utility/base_from_member.hpp>
#include <ostream>

// No hand-rolled FixedBufBase needed
class FixedStream
    : private boost::base_from_member<FixedBuf>   // initialized 1st; .member is the FixedBuf
    , public  std::ostream
{
    using Carrier = boost::base_from_member<FixedBuf>;
public:
    FixedStream()
        : Carrier{}
        , std::ostream(&Carrier::member)   // C++11 β€” safe, Carrier is alive
    {}

    std::string_view view() const { return Carrier::member.view(); }  // C++17
};

The discriminator N resolves ambiguity when two carriers share the same type. boost::base_from_member<std::stringbuf, 0> and boost::base_from_member<std::stringbuf, 1> are distinct types, so the linker and the compiler can tell them apart without any hand-rolled tag struct.

cpp
// C++11 β€” two same-type carriers, no custom TaggedBase needed
class DualStream
    : private boost::base_from_member<std::stringbuf, 0>   // input
    , private boost::base_from_member<std::stringbuf, 1>   // output
    , public  std::iostream
{
    using InCarrier  = boost::base_from_member<std::stringbuf, 0>;
    using OutCarrier = boost::base_from_member<std::stringbuf, 1>;
public:
    DualStream()
        : InCarrier{}, OutCarrier{}
        , std::iostream(nullptr)
    {}
};

To integrate Boost in a CMake project, locate it and link against the header-only interface target:

cmake
# CMakeLists.txt
find_package(Boost REQUIRED)
target_link_libraries(my_target PRIVATE Boost::headers)

Making the Derived Class Movable/Copyable

If the carrier holds a move-only resource such as a std::unique_ptr or a socket handle, the derived class must explicitly declare its move operations. The implicitly generated ones may not be correct or may be suppressed entirely.

cpp
// C++11 β€” rule of five for a base-from-member class
struct HandleBase {
    explicit HandleBase(std::unique_ptr<Resource> r) : resource_(std::move(r)) {}
    std::unique_ptr<Resource> resource_;  // move-only
};

class ManagedLogger
    : private HandleBase
    , public  LoggerBase
{
public:
    explicit ManagedLogger(std::unique_ptr<Resource> r)
        : HandleBase(std::move(r))
        , LoggerBase(HandleBase::resource_.get())
    {}

    // Copy is impossible β€” resource_ is unique
    ManagedLogger(const ManagedLogger&)            = delete;   // C++11
    ManagedLogger& operator=(const ManagedLogger&) = delete;   // C++11

    // Move: default is correct only if no base stores a raw pointer into the carrier
    ManagedLogger(ManagedLogger&&)            = default;        // C++11
    ManagedLogger& operator=(ManagedLogger&&) = default;        // C++11

    ~ManagedLogger() = default;
};

Pitfall: a dependent base may store a raw pointer into the carrier's member. std::ostream stores a std::streambuf* pointer set during construction. After a move the pointer still addresses the old object's buffer, which is now in a valid-but-unspecified state.

cpp
// C++11 β€” fix up the internal pointer after moving a stream-based class
class FixedStream
    : private FixedBufBase
    , public  std::ostream
{
public:
    FixedStream() : FixedBufBase{}, std::ostream(&buf) {}

    FixedStream(FixedStream&& other)
        : FixedBufBase(std::move(other))       // move the buffer
        , std::ostream(std::move(other))       // move the stream state
    {
        // Re-establish the pointer: std::ostream::rdbuf() is the fix-up call
        rdbuf(&buf);                           // C++11 β€” points into *this, not other
    }

    FixedStream& operator=(FixedStream&& other) {
        FixedBufBase::operator=(std::move(other));
        std::ostream::operator=(std::move(other));
        rdbuf(&buf);                           // C++11 β€” re-establish after assignment
        return *this;
    }
};

Carrier base inherits the resource's move semantics. If HandleBase contains only a std::unique_ptr, it is automatically move-only; no extra machinery is needed in the carrier itself β€” the derived class simply propagates the constraint.

Verifying Initialization Order at Compile Time

C++ does not emit a compiler warning when the carrier and dependent base are listed in the wrong order. The resulting undefined behaviour is silent and may go undetected until a production crash. Defensive verification costs nothing in release builds.

Static verification via static_assert in the carrier constructor. If the carrier's constructor runs before the dependent base, sizeof(*this) within the carrier body reflects only the carrier subobject β€” which can be asserted to be greater than zero as a smoke check, though the more useful compile-time check is to verify type relationships:

cpp
// C++11 β€” static assertion in the carrier to document the contract
template<typename Derived>
struct BufCarrier {
    std::stringbuf buf;
    BufCarrier() {
        // Confirm at compile time that Derived does inherit from this carrier
        static_assert(std::is_base_of<BufCarrier, Derived>::value,
                      "BufCarrier must appear in Derived's base-class list");
    }
};

Runtime verification in debug builds. A simple boolean flag set by the carrier and checked by the dependent base catches ordering mistakes early:

cpp
// C++11 β€” debug-mode runtime check
#ifndef NDEBUG
struct BufBase {
    BufBase() : initialized_(true) {}
    bool initialized_;
};

struct DependentBase {
    explicit DependentBase(const BufBase& b) {
        assert(b.initialized_ && "BufBase must be initialized before DependentBase");
    }
};
#endif

AddressSanitizer and UBSan catch many real ordering violations automatically. Compiling with -fsanitize=address,undefined often surfaces reads of uninitialized base subobjects before any manual assertion fires; it should be part of the regular CI pipeline for any code using this idiom.

Base-from-Member with CRTP

CRTP bases occasionally need to flush, finalize, or otherwise touch a resource that the derived class owns. Because the CRTP base destructor runs before the derived object's members are destroyed, the resource must outlive the CRTP base β€” exactly the situation base-from-member solves.

Pattern: promote the resource into a carrier that precedes the CRTP base in the inheritance list. The resource is then alive throughout the CRTP base's entire lifetime, including its destructor.

cpp
// C++11 β€” three-level hierarchy: ResourceCarrier β†’ CrtpBase<Derived> β†’ Derived
template<typename D>
class CrtpBase {
protected:
    // Destructor needs derived's resource to still be alive
    ~CrtpBase() {
        static_cast<D&>(*this).resource_.flush();   // C++11 β€” resource is in carrier, still alive
    }
};

// Carrier for the resource
struct ResourceCarrier {
    explicit ResourceCarrier(const char* path) : resource_(path) {}
    SomeResource resource_;
};

// Final class: carrier first, CRTP base second, no independent resource member needed
class ConcreteLogger
    : private ResourceCarrier                 // initialized 1st β€” resource_ is alive
    , public  CrtpBase<ConcreteLogger>        // initialized 2nd β€” CRTP base safe to use resource_
{
public:
    explicit ConcreteLogger(const char* path)
        : ResourceCarrier(path)
        , CrtpBase<ConcreteLogger>{}
    {}
};

The CRTP base accesses static_cast<Derived&>(*this).resource_ β€” but that field lives in the carrier, not in Derived directly. This is intentional: the carrier guarantees the field outlives the CRTP base, while the CRTP base accesses it through the derived reference with no lifetime hazard.

cpp
// C++11 β€” CRTP policy base that flushes a buffer owned by the carrier
template<typename Derived>
struct AutoFlushBase {
    ~AutoFlushBase() {
        // Safe only because the buffer carrier is declared before this base
        static_cast<Derived*>(this)->buf.pubsync();
    }
};

class BufferedStream
    : private boost::base_from_member<FixedBuf>   // carrier β€” FixedBuf alive first
    , public  AutoFlushBase<BufferedStream>        // CRTP β€” destructs before carrier
    , public  std::ostream
{
    using Carrier = boost::base_from_member<FixedBuf>;
public:
    BufferedStream()
        : Carrier{}, AutoFlushBase<BufferedStream>{}, std::ostream(&Carrier::member)
    {}

    FixedBuf& buf = Carrier::member;  // alias for CRTP base's access
};

Why Destructors Work Correctly

Destruction order in C++ is the strict reverse of construction order: member subobjects destruct first (in reverse declaration order), then base subobjects (in reverse base-class-list order). For a base-from-member hierarchy this means:

  1. The derived class's own members destruct.
  2. The dependent base (std::ostream) destructs β€” it can still write to the streambuf.
  3. The carrier (FixedBufBase) destructs β€” the buffer goes away last.

This is not a coincidence: the idiom was designed so that the destruction invariant mirrors the construction invariant. The buffer is created first and destroyed last, bracketing the dependent base's lifetime entirely.

cpp
// C++11 β€” confirm destruction order with diagnostic output
struct BufBase {
    BufBase()  { std::printf("BufBase::ctor\n");  }
    ~BufBase() { std::printf("BufBase::dtor\n");  }   // runs LAST
    std::stringbuf buf;
};

struct StreamBase : std::ostream {
    explicit StreamBase(std::streambuf* sb) : std::ostream(sb) {
        std::printf("StreamBase::ctor\n");
    }
    ~StreamBase() { std::printf("StreamBase::dtor\n"); }  // runs before BufBase::dtor
};

class DiagStream
    : private BufBase       // constructed 1st, destroyed LAST
    , public  StreamBase    // constructed 2nd, destroyed 1st
{
public:
    DiagStream() : BufBase{}, StreamBase(&buf) {}
};

// Output when DiagStream goes out of scope:
//   BufBase::ctor
//   StreamBase::ctor
//   StreamBase::dtor   ← flushes to buf β€” buf is still alive
//   BufBase::dtor      ← buf goes away after stream is done

Pitfall: when multiple carriers depend on one another, declare them in dependency order. If CarrierB requires a resource from CarrierA, CarrierA must appear first in the base-class list. Destruction then tears down CarrierB before CarrierA, preserving the invariant.

cpp
// C++11 β€” two-carrier chain: A must precede B
class TwoResourceStream
    : private CarrierA          // provides raw socket β€” destroyed LAST
    , private CarrierB          // wraps socket in a buffer β€” destroyed before A
    , public  std::ostream      // destroyed first
{
public:
    TwoResourceStream()
        : CarrierA{}
        , CarrierB(CarrierA::socket)   // safe: A is alive
        , std::ostream(&CarrierB::buf) // safe: B is alive
    {}
};

Alternative: std::optional<T> as a Late-Initialized Member (C++17)

When the "dependent base" does not genuinely need to be a base class β€” no virtual dispatch through this, no std::is_base_of check β€” C++17 offers a simpler pattern: hold the dependent object as a std::optional member and emplace it in the constructor body after all other members are fully constructed.

cpp
// C++17 β€” optional member initialized after resource_, no inheritance required
#include <optional>

class LoggerAlt {
    SomeResource       resource_;         // initialized 1st by member-init list
    std::optional<DependentHelper> dep_;  // initially empty

public:
    explicit LoggerAlt(const char* path)
        : resource_(path)                 // resource_ is fully constructed here
    {
        dep_.emplace(resource_);          // C++17 β€” constructor body runs after all members
    }

    void write(std::string_view msg) {
        dep_->write(msg);                 // access via operator->
    }
};

Trade-off: DependentHelper is not a real base class. Code that calls dep_->method() must go through the optional, not through this; virtual functions in DependentHelper will not automatically dispatch when a LoggerAlt* is passed where a DependentHelper* is expected.

When std::optional works: when DependentHelper is an implementation detail, there is no polymorphic interface requirement, and std::is_base_of<DependentHelper, LoggerAlt> need not hold.

When it fails: when the surrounding framework relies on static_cast<DependentHelper*>(this), when virtual functions must be overridden in LoggerAlt and dispatched through DependentHelper's vtable, or when library code stores a DependentHelper& reference to *this.

cpp
// C++17 β€” optional fails here: library needs DependentHelper* == this
void register_with_framework(DependentHelper& h);   // expects a real base

class Bad {
    SomeResource       resource_;
    std::optional<DependentHelper> dep_;
public:
    Bad() : resource_{} { dep_.emplace(resource_); }
    void init() {
        register_with_framework(*dep_);  // ← passes &dep_.value(), NOT this
    }                                    //   framework stores wrong pointer
};

// Correct: use base-from-member so this == (DependentHelper*)this
class Good
    : private ResourceCarrier
    , public  DependentHelper       // real base β€” this IS a DependentHelper
{
public:
    Good() : ResourceCarrier{}, DependentHelper(ResourceCarrier::resource_) {
        register_with_framework(*this);  // ← correct: this is a DependentHelper
    }
};

See Also

  • std::basic_ios and std::basic_streambuf in <ios> β€” the Standard Library's canonical use of this idiom
  • Empty Base Optimization (EBO) β€” related technique that eliminates the size overhead of stateless base classes
  • [[no_unique_address]] (C++20, <type_traits> context) β€” zero-size member alternative for stateless policies
  • boost::base_from_member<T, N> in <boost/utility/base_from_member.hpp> β€” reusable parameterized carrier with discriminator support