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++98A 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.
// 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.
// 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:
// 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
#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.
#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:
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.
// 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.
class MyStream
: private BufBase // must precede ostream β base-from-member
, public std::ostreamCommon 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.
// 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.
// 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:
# 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.
// 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.
// 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:
// 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:
// 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");
}
};
#endifAddressSanitizer 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.
// 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.
// 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:
- The derived class's own members destruct.
- The dependent base (
std::ostream) destructs β it can still write to thestreambuf. - 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.
// 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 donePitfall: 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.
// 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.
// 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.
// 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_iosandstd::basic_streambufin<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 policiesboost::base_from_member<T, N>in<boost/utility/base_from_member.hpp>β reusable parameterized carrier with discriminator support