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.
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:
uses_allocator
/allocator_arg_t
support)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
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.
In reply to the comments, here's a sample outlining some of the features mentioned above and in particular the use of piecewise emplacement:
#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")}