This is arguably a bug in the C++ standard library, but the reason it hits KJ particularly hard is because of KJ's philosophy about constness:
In particular:
- Constness should be transitive. This implies that copying from a const value can only be allowed when the copy is a deep copy. Otherwise, making a copy would discard the transitive constness on the shared backing objects. Cap'n Proto `Client` objects are copy-by-refcount, so shallow copies, hence cannot be const copies.
- Constness should imply thread safety. Cap.'n Proto `Client` objects are refcounted, and the refcounting is not thread-safe (since thread-safe refcounting is very slow and these objects are tied to a thread-local event loop anyway).
Unfortunately, the C++ standard library mostly takes the view that `const` is shallow. To be fair, this is consistent with how built-in pointer and reference types work, but it is also a much less useful way of using const.
Annoyingly, the C++ standard library containers work just fine with move-only types, but choke on types that are non-const-copyable. These containers really ought to default to using move semantics, or automatically fall back to it when the copy constructor is non-const. I consider it a bug in the standard library implementation that it errors instead.
Luckily, you can work around the problem by wrapping the type in a struct that only has a move constructor, not a copy constructor, which forces the library to use move semantics only:
struct Wrapper<T> {
Wrapper(Wrapper&) = delete;
Wrapper(const Wrapper&) = delete;
Wrapper(Wrapper&&) = default;
T value;
}
Now you can use `std::map<string,Wrapper<Foobar::Client>>`.
Another alternative is to use `kj::HashMap`, which doesn't suffer these problems, and is faster than `std::unordered_map`.
-Kenton