diff --git a/universal/include/userver/utils/algo.hpp b/universal/include/userver/utils/algo.hpp index 032fa3a54ec0..f4ad4000da3e 100644 --- a/universal/include/userver/utils/algo.hpp +++ b/universal/include/userver/utils/algo.hpp @@ -57,7 +57,8 @@ auto* FindOrNullptr(Container& container, const Key& key) { template auto FindOrDefault(Container& container, const Key& key, Default&& def) { const auto* ptr = USERVER_NAMESPACE::utils::FindOrNullptr(container, key); - return (ptr ? *ptr : decltype(*ptr){std::forward(def)}); + using R = std::remove_cvref_t; + return (ptr ? *ptr : R(std::forward(def))); } /// @brief Returns default value if no key in associative container, otherwise @@ -65,7 +66,8 @@ auto FindOrDefault(Container& container, const Key& key, Default&& def) { template auto FindOrDefault(Container& container, const Key& key) { const auto* ptr = USERVER_NAMESPACE::utils::FindOrNullptr(container, key); - return (ptr ? *ptr : decltype(*ptr){}); + using R = std::remove_cvref_t; + return (ptr ? *ptr : R()); } /// @brief Returns std::nullopt if no key in associative container, otherwise diff --git a/universal/src/utils/algo_test.cpp b/universal/src/utils/algo_test.cpp index 28d1005e9d33..e0ac26976154 100644 --- a/universal/src/utils/algo_test.cpp +++ b/universal/src/utils/algo_test.cpp @@ -1,6 +1,8 @@ #include +#include #include +#include #include #include #include @@ -48,21 +50,26 @@ TEST(UtilsAlgo, FindOrNullptrSets) { TEST(UtilsAlgo, FindOrDefaultMaps) { constexpr int kFallback = 42; + constexpr int kDefault = int(); std::map m = {{"1", 2}}; std::unordered_map um = {{"1", 2}}; EXPECT_EQ(utils::FindOrDefault(m, "2", kFallback), kFallback); EXPECT_EQ(utils::FindOrDefault(um, "2", kFallback), kFallback); - ASSERT_EQ(utils::FindOrDefault(m, "1", kFallback), 2); - ASSERT_EQ(utils::FindOrDefault(um, "1", kFallback), 2); - EXPECT_EQ(utils::FindOrDefault(m, "1", kFallback), 2); EXPECT_EQ(utils::FindOrDefault(um, "1", kFallback), 2); + + EXPECT_EQ(utils::FindOrDefault(m, "2"), kDefault); + EXPECT_EQ(utils::FindOrDefault(um, "2"), kDefault); + + EXPECT_EQ(utils::FindOrDefault(m, "1"), 2); + EXPECT_EQ(utils::FindOrDefault(um, "1"), 2); } TEST(UtilsAlgo, FindOrDefaultSets) { constexpr int kFallback = 42; + constexpr int kDefault = int(); std::set s = {1}; std::unordered_set us = {1}; @@ -71,6 +78,62 @@ TEST(UtilsAlgo, FindOrDefaultSets) { EXPECT_EQ(utils::FindOrDefault(s, 1, kFallback), 1); EXPECT_EQ(utils::FindOrDefault(us, 1, kFallback), 1); + + EXPECT_EQ(utils::FindOrDefault(s, 2), kDefault); + EXPECT_EQ(utils::FindOrDefault(us, 2), kDefault); + + EXPECT_EQ(utils::FindOrDefault(s, 1), 1); + EXPECT_EQ(utils::FindOrDefault(us, 1), 1); +} + +TEST(UtilsAlgo, FindOrDefaultNoList) { + struct List { + int val = 0; + + List() = default; + List(const List&) = default; + explicit List(int v) : val(v) {} + List(std::initializer_list) : val(-1) {} + + auto operator<=>(const List&) const = default; + }; + + constexpr int kFallback = 42; + constexpr int kDefault = List().val; + + std::map m; + std::set s; + + EXPECT_TRUE(m.empty()); + EXPECT_EQ(utils::FindOrDefault(m, 0, kFallback).val, kFallback); + EXPECT_EQ(utils::FindOrDefault(m, 0).val, kDefault); + + EXPECT_TRUE(s.empty()); + EXPECT_EQ(utils::FindOrDefault(s, List(1), kFallback).val, kFallback); + EXPECT_EQ(utils::FindOrDefault(s, List(1)).val, kDefault); +} + +TEST(UtilsAlgo, FindOrDefaultNoExtraCopies) { + struct NoCopies { + bool copy = false; + + NoCopies() = default; + NoCopies(const NoCopies&) : copy(true) {} + NoCopies(NoCopies&& that) noexcept : copy(that.copy) {} + + auto operator<=>(const NoCopies&) const = default; + }; + + std::map m; + std::set s; + + EXPECT_TRUE(m.empty()); + EXPECT_FALSE(utils::FindOrDefault(m, 0, NoCopies()).copy); + EXPECT_FALSE(utils::FindOrDefault(m, 0).copy); + + EXPECT_TRUE(s.empty()); + EXPECT_FALSE(utils::FindOrDefault(s, NoCopies(), NoCopies()).copy); + EXPECT_FALSE(utils::FindOrDefault(s, NoCopies()).copy); } TEST(UtilsAlgo, FindOptionalMaps) {