Search code examples
c++boosthashstlset

C++ STD Unordered Set/Map vs Boost Unordered Set/Map


What are the differences between them, and when should you use each?

I have tried a few tests on an old laptop and there seems to be no significant performance difference for storing basic types like ints and longs. I think one of the main difference is boost container emplace methods dont support std::piecewise_construct and tuples, which causes additional overhead.

Edit: the stuff I'm working on already has a lot of boost features, so I'm not worried about compatibility issues with boost libraries.


Solution

  • The Boost ones have some features that do not exist in the standard library. Off the top of my head:

    • Boost Hash, which is more flexible and easier to customize than specializing std::hash<> (though specializing boost::hash<> is also supported; the easier route is to implement a inline friend size_t hash_value(T const&) which will "magically" be picked up by the default hash<> implementation

    • Boost tends to support heterogeneous lookup better (look for extended find/insert signatures)

    • The ordered versions may have extra constructors to efficiently construct over known ordered sequences

    • In general Boost containers (including others from the Boost Container library) have more guarantees/options:

      • (better) support for stateful allocators (including scoped_allocator_adaptor, so with full uses_allocator/allocator_arg_t support)
      • constructors don't allocate
      • some support for incomplete types in the the template arguments
    • As far as I know piecewise construction is perfectly fine in Boost. E.g. Change notes lists for 1.48.0:

      * `emplace` used to emulate the variadic pair constructors that
        appeared in early C++0x drafts. Since they were removed it no
        longer does so. It does emulate the new `piecewise_construct`
        pair constructors - only you need to use
        `boost::piecewise_construct`. To use the old emulation of
        the variadic constructors define
      

    Summary

    I don't expect significant differences in performance.

    Quality of implementation differences will exist. Boost's will probably be a bit slower to compile and support older compiler versions.

    BONUS

    In reply to the comments, here's a sample outlining some of the features mentioned above and in particular the use of piecewise emplacement:

    Live On Compiler Explorer

    #include <boost/unordered_map.hpp>
    #include <iomanip>
    #include <fmt/ranges.h>
    #include <fmt/ostream.h>
    
    struct MyKey {
        MyKey(int i, std::string s) : _i(i), _s(std::move(s)) {}
    
        bool operator==(MyKey const&) const = default;
    
      private:
        int _i;
        std::string _s;
    
        friend size_t hash_value(MyKey const& mk) {
            using boost::hash_value;
            size_t seed = hash_value(mk._i);
            boost::hash_combine(seed, hash_value(mk._s));
            return seed;
        }
    
        friend auto& operator<<(auto& os, MyKey const& mk) {
            return os << "[" << mk._i << ", " << std::quoted(mk._s) << "]";
        }
    };
    
    int main() {
        boost::unordered_map<MyKey, std::string> m;
    
        m.emplace(boost::unordered::piecewise_construct,
                  boost::make_tuple(42, "forty-two"),
                  boost::make_tuple("the answer"));
    
        m.emplace(std::piecewise_construct,
                  std::/*make_*/tuple(43, "forty-three"),
                  std::/*make_*/tuple("not the answer"));
    
        fmt::print("m: {}\n", m);
    }
    

    Prints

    m: {([43, "forty-three"], "not the answer"), ([42, "forty-two"], "the answer")}