Split get_default() into two for deferred default construction
authorAaryaman Sagar <aary@instagram.com>
Mon, 20 Nov 2017 23:59:34 +0000 (15:59 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Tue, 21 Nov 2017 00:07:16 +0000 (16:07 -0800)
Summary:
As it stood currently folly::get_default() would unnecessarily
construct a value into the third parameter, which was unnecessary in the fast
path where the element was found in the map

Reviewed By: yfeldblum

Differential Revision: D6366352

fbshipit-source-id: db55b944ca63e565997094c11b90c4ebe98531ce

folly/MapUtil.h
folly/test/MapUtilTest.cpp

index b1fbfadcaf59ecf3db6d14e80f35e472152fa249..b95da417ffe3f353dde541e6845b201ba16806da 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <folly/Conv.h>
 #include <folly/Optional.h>
 
 #include <folly/Conv.h>
 #include <folly/Optional.h>
+#include <folly/functional/Invoke.h>
 #include <tuple>
 
 namespace folly {
 #include <tuple>
 
 namespace folly {
@@ -26,13 +27,27 @@ namespace folly {
  * Given a map and a key, return the value corresponding to the key in the map,
  * or a given default value if the key doesn't exist in the map.
  */
  * Given a map and a key, return the value corresponding to the key in the map,
  * or a given default value if the key doesn't exist in the map.
  */
-template <class Map, typename Key = typename Map::key_type>
-typename Map::mapped_type get_default(
-    const Map& map,
-    const Key& key,
-    const typename Map::mapped_type& dflt = typename Map::mapped_type()) {
+template <typename Map, typename Key>
+typename Map::mapped_type get_default(const Map& map, const Key& key) {
   auto pos = map.find(key);
   auto pos = map.find(key);
-  return (pos != map.end() ? pos->second : dflt);
+  return (pos != map.end()) ? (pos->second) : (typename Map::mapped_type{});
+}
+template <
+    class Map,
+    typename Key = typename Map::key_type,
+    typename Value = typename Map::mapped_type,
+    typename std::enable_if<!is_invocable<Value>::value>::type* = nullptr>
+typename Map::mapped_type
+get_default(const Map& map, const Key& key, Value&& dflt) {
+  auto pos = map.find(key);
+  if (pos != map.end()) {
+    return pos->second;
+  } else {
+    // if elision from function parameters was allowed, then we could make the
+    // third parameter a value parameter and just elide that into the return
+    // value, but sadly that is not allowed (yet)
+    return std::forward<Value>(dflt);
+  }
 }
 
 /**
 }
 
 /**
index 52e63e81f7a900360a54a6d3ccd5f0fc1cf2f4be..693f7235260c83bdac27e893d424198db93cea6a 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <folly/MapUtil.h>
 
 
 #include <folly/MapUtil.h>
 
+#include <cstddef>
 #include <map>
 #include <unordered_map>
 
 #include <map>
 #include <unordered_map>
 
@@ -238,6 +239,7 @@ struct GetRefDefaultPathCompiles<
         std::declval<int>(),
         std::declval<int>(),
         std::declval<T>()))>> : std::true_type {};
         std::declval<int>(),
         std::declval<int>(),
         std::declval<T>()))>> : std::true_type {};
+
 } // namespace
 
 TEST(MapUtil, get_ref_default_path_temporary) {
 } // namespace
 
 TEST(MapUtil, get_ref_default_path_temporary) {
@@ -246,3 +248,41 @@ TEST(MapUtil, get_ref_default_path_temporary) {
   EXPECT_FALSE(GetRefDefaultPathCompiles<const int&&>::value);
   EXPECT_FALSE(GetRefDefaultPathCompiles<int&&>::value);
 }
   EXPECT_FALSE(GetRefDefaultPathCompiles<const int&&>::value);
   EXPECT_FALSE(GetRefDefaultPathCompiles<int&&>::value);
 }
+
+namespace {
+
+class TestConstruction {
+ public:
+  static std::size_t numberDefaultConstructs;
+  TestConstruction() {
+    ++numberDefaultConstructs;
+  }
+  TestConstruction(TestConstruction&&) = default;
+  TestConstruction(const TestConstruction&) = default;
+
+  TestConstruction& operator=(const TestConstruction&) = delete;
+  TestConstruction& operator=(TestConstruction&&) = delete;
+};
+
+std::size_t TestConstruction::numberDefaultConstructs = 0;
+
+} // namespace
+
+TEST(MapUtil, test_get_default_deferred_construction) {
+  auto map = std::unordered_map<int, TestConstruction>{};
+  map.insert({1, TestConstruction{}});
+
+  EXPECT_EQ(TestConstruction::numberDefaultConstructs, 1);
+
+  {
+    auto val = get_default(map, 1);
+    EXPECT_EQ(TestConstruction::numberDefaultConstructs, 1);
+    static_cast<void>(val);
+  }
+
+  {
+    auto val = get_default(map, 1, TestConstruction{});
+    EXPECT_EQ(TestConstruction::numberDefaultConstructs, 2);
+    static_cast<void>(val);
+  }
+}