Skip to main content

Boost and the C++ Standard

If boost::shared_ptr and std::shared_ptr look almost identical, that is because one is the ancestor of the other. Boost has been the C++ standard library's primary incubator for two decades: ideas are built and battle-tested in Boost, then proposed to the ISO committee, and the best of them graduate into std. Understanding this lineage is the key to a practical question every modern C++ developer faces — should I use boost::X or std::X?

Why this matters

Many Boost libraries now have a standard-library twin. On a current toolchain the std:: version is usually the right default: no extra dependency, no link step, and it is guaranteed to be there. Reach for the Boost version when you need a feature std lacks, an older compiler forces your hand, or Boost simply offers more.

The lineage from Boost to std

Boost components have flowed into the standard in three big waves, plus a steady trickle.

  • C++11 absorbed the smart-pointer and concurrency groundwork: shared_ptr, weak_ptr, thread, mutex, bind, function, array, tuple, regex, chrono, the <random> engines, and much of <type_traits>.
  • C++17 absorbed the vocabulary types and filesystem: optional, variant, any, and std::filesystem.
  • Beyond that, individual pieces continue to migrate, and proposals routinely cite Boost experience as evidence that a design works in the field.

This is the same pipeline described in history and philosophy: build in Boost, prove it in real projects, then standardise.

A comparison table

Boost componentStandard equivalentFirst standardisedNotes on the migration
boost::shared_ptr / weak_ptrstd::shared_ptr / weak_ptrC++11Near-identical; Boost adds intrusive_ptr and more.
boost::scoped_ptrstd::unique_ptrC++11unique_ptr is strictly more capable (movable).
boost::functionstd::functionC++11Same idea; interfaces line up closely.
boost::bindstd::bind / lambdasC++11Prefer lambdas in new code.
boost::arraystd::arrayC++11Direct adoption.
boost::tuplestd::tupleC++11std adds structured bindings support.
boost::regexstd::regexC++11Boost.Regex is often faster and more featureful.
boost::chronostd::chronoC++11std::chrono greatly expanded in C++20.
boost::random<random>C++11Designs are closely related.
boost::optionalstd::optionalC++17Boost allows optional<T&>; std does not.
boost::variantstd::variantC++17Different empty/exception semantics (see below).
boost::anystd::anyC++17Near drop-in.
boost::filesystemstd::filesystemC++17std version derived almost directly from Boost.
boost::threadstd::threadC++11Boost adds interruption, thread groups, more.
boost::unordered_mapstd::unordered_mapC++11Boost added open-addressing variants later.

How to choose today

A practical decision procedure:

  1. Does a std:: equivalent exist and does your toolchain support it? If yes, default to std::. You avoid a dependency, avoid a possible link step, and get something guaranteed present.
  2. Are you stuck on an older standard or compiler? If you must target C++14 but want optional, or a platform whose std::filesystem is incomplete, the Boost version backfills the gap.
  3. Does Boost offer something std does not? Boost.Regex outperforms many std::regex implementations; Boost.Thread has interruption and thread groups; Boost.Optional supports references. In those cases Boost is a deliberate choice, not a fallback.
  4. Are you already deep in a Boost subsystem? If you use Boost.Asio or Boost.Serialization heavily, staying within the Boost types it expects can be simpler than converting at every boundary.
choosing_optional.cpp
// Modern toolchain: prefer std
#include <optional>
std::optional<int> find_std();

// Need optional<T&>, which std::optional does not support:
#include <boost/optional.hpp>
int value = 0;
boost::optional<int&> ref = value; // a rebindable reference-like optional
Default to std, escalate to Boost

A good rule of thumb: write new code against std::, and reach for boost:: only when you hit a concrete limitation. This keeps dependencies lean while leaving Boost available for the cases that need it.

Subtle behavioural differences

The twins are similar, not identical. A few differences that bite during migration:

  • variant and the empty state. boost::variant never holds "nothing" in normal use but can briefly during assignment, and it has different exception-safety guarantees. std::variant has an explicit valueless_by_exception() state. Code that pattern-matches on a variant may need adjusting.
  • optional references. boost::optional<T&> is legal and rebindable; std::optional<T&> is ill-formed. Replacing one with the other can fail to compile.
  • Visitation syntax. boost::apply_visitor(visitor, v) versus std::visit(visitor, v) — both exist but the call shapes differ.
  • Smart-pointer extras. boost::shared_ptr historically offered features (such as certain aliasing and atomic-access helpers) on timelines that differed from std. The cores match, the edges do not.
  • filesystem error reporting. Both offer throwing and error_code overloads, but exact exception types and a few path-handling corner cases differ between the Boost and std versions.
Migration is rarely a pure find-and-replace

Swapping boost:: for std:: across a codebase usually compiles for the easy 90% and then fails on these edge cases. Migrate a component at a time and lean on your test suite, especially around variant exception safety and optional<T&>.

Boost keeps moving too

Standardisation does not freeze the Boost version. Boost often continues to evolve a library after its std twin ships — adding features, performance work, or new variants — which is another reason the two can diverge over time.

Where to go next