4
\$\begingroup\$

dict objects in Python have an update method that can be used to extend one dictionary with all items from another, possibly overwriting entries that already exist. I find this function very useful from time to time.

The associative containers from the C++ standard library (std::map and std::unordered_map) do not provide a direct analogue for this functionality so I wrote the following utility functions.

Comments regarding any aspect of the code are welcome.

#include <cassert>
#include <utility>
namespace codereview
{
 template <typename DictT>
 void update(DictT& dst, const DictT& src)
 {
 for (const auto& [key, value] : src) {
 dst.insert_or_assign(key, value);
 }
 }
 template <typename DictT>
 void update(DictT& dst, DictT&& src) noexcept
 {
 // Equal allocators are a precondition for splicing nodes
 assert(src.get_allocator() == dst.get_allocator());
 dst.merge(src);
 while (!src.empty()) {
 auto node = src.extract(src.cbegin());
 dst.extract(node.key());
 dst.insert(std::move(node));
 }
 }
} // namespace codereview
asked Nov 23, 2017 at 16:37
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I asked about the weird interface on SO. Feel free to join in guys, this looks really interesting. Might as well be a bug. \$\endgroup\$ Commented Nov 23, 2017 at 20:56

1 Answer 1

3
\$\begingroup\$

You're not thinking laterally here. If you std::swap() your two inputs, then you can merge() the original into the new, and the contents of the new map will prevail.

Here's what I mean:

namespace codereview
{
 template <typename DictT>
 void update(DictT& dst, DictT src)
 {
 std::swap(dst, src);
 dst.merge(src);
 }
} // namespace codereview
// Test
#include <iostream>
#include <map>
template<typename Map>
void print(const Map& x)
{
 for (auto const& i: x)
 std::cout << i.first << "=" << i.second << " ";
 std::cout << std::endl;
}
int main()
{
 using Map = std::map<int, const char*>;
 Map a { {0, "zero"}, {1, "one"} };
 const Map b { {0, "nothing"}, {2, "two"}};
 print(a);
 // test lvalue argument
 codereview::update(a, b);
 print(a);
 // test rvalue argument
 codereview::update(a, Map{{3, "three"}, {0, "nada"}});
 print(a);
}

Output

0=zero 1=one 
0=nothing 1=one 2=two 
0=nada 1=one 2=two 3=three 

Note that I changed the method signature to accept src by copy - the compiler will move-construct into src if it can, but if not, we'll need a copy so that we don't perturb the caller's version when we swap.

answered Nov 23, 2017 at 17:39
\$\endgroup\$
2
  • \$\begingroup\$ I see now. Really good solution. I kind of ignored effects of swap \$\endgroup\$ Commented Nov 23, 2017 at 18:00
  • \$\begingroup\$ Nice! This solution is indeed more elegant and probably also more efficient than mine. \$\endgroup\$ Commented Nov 24, 2017 at 14:07

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.