【问题标题】:C++ STL: std::find with std::mapC++ STL:std::find 与 std::map
【发布时间】:2017-07-18 01:42:16
【问题描述】:

Python 允许您编写if e in arr: ... if key in dict: ...,这很方便。

我们可以使用 std::find()std::map 对后者做类似的事情吗?这将允许我使用单个通用函数统一处理 std::arraystd::map,而无需显式切换到 std::map::find()

但如果重载operator==()是唯一的办法,我宁愿放弃这个想法......

更新:请注意我已经有一种解决方案

“重载operator==()”我的意思是这样的:

template<typename K>
struct KF {
    K&& k;

    template <typename V>
    friend bool operator==(const typename std::pair<const K, V>& pair, const KF<K>& o) {
        return pair.first == o.k;
    }

};

template <typename K>
KF<K> keyFinder(K&& k) { return KF<K>{ std::forward<K>(k) }; }

int main() {
    std::set<int> s{ 1, 2, };
    cout << (std::find(s.begin(), s.end(), 1) == s.end()) << endl; // => 0
    cout << (std::find(s.begin(), s.end(), 3) == s.end()) << endl; // => 1

    std::map<int, int> m{ {1,10}, {2,20}, };
    cout << (std::find(m.begin(), m.end(), keyFinder(1)) == m.end()) << endl; // => 0
    cout << (std::find(m.begin(), m.end(), keyFinder(3)) == m.end()) << endl; // => 1
}

当我们以通用方式处理非标量K(完美转发?)时,事情会变得更加复杂

【问题讨论】:

    标签: c++ algorithm dictionary stl iterator


    【解决方案1】:

    ...为什么不编写自己的实用函数?

    template <typename TContainer, typename TValue>
    bool contains(const TContainer& c, const TValue& x);
    

    你可以使用重载来匹配容器:

    template <typename TValue, std::size_t N>
    bool contains(const std::array<TValue, N>& c, const TValue& x)
    {
        return std::find(std::begin(c), std::end(c), x) != std::end(c);
    }
    
    template <typename TValue, typename... Ts>
    bool contains(const std::map<Ts...>& c, const TValue& x)
    {
        return c.find(x) != std::end(c);
    }
    

    用法:

    std::array<int, 2> a{1,2};
    std::map<int, int> b{{1,2},{3,4}};
    
    assert(contains(a, 1));
    assert(!contains(a, 42));
    assert(contains(b, 1));
    assert(!contains(b, 42));
    

    live example on wandbox


    如果您希望将来支持其他容器,最好使用 SFINAE 来检查特定表达式是否有效。这种方法效果很好,因为它不关心容器的类型,只关心可以对其执行哪些操作。

    detection idiom 可能会让通过 SFINAE 检查成员可用性变得非常容易(并且它的实现与 C++11 兼容)

    我还写了一篇关于检查表达式有效性 in-situ with C++17 的文章,这可能是一篇有趣的文章。尽管标题如此,但它涵盖了检查表达式有效性的 C++11、C++14 和 C++17 技术:

    "checking expression validity in-place with C++17"

    【讨论】:

    • 这就是我所说的“显式切换到std::map::find()”的意思……如果我想支持unorderd_mapmultimap 以及像spp::sparse_hash_map 这样的第三方库怎么办?检查SFINAE是否有M::find
    • @nodakai:这可行——detection idiom 可能会很容易通过 SFINAE 检查成员可用性(并且它的实现与 C++11 兼容)。
    • @nodakai:我在回答中添加了有关 SFINAE 方法的其他信息 - 如果您有更多问题,请告诉我。
    【解决方案2】:

    要回答您的明确问题 - 不,std::find 将无法统一为 std::map/std::unordered_mapstd::array/std::vector 工作,因为前者是键/值对的集合和后者是值的集合...

    您可能想改用std::find_if,因为它可以让您在定义相等条件时更加灵活,例如像这样(c++1z 方法):

    #include <array>
    #include <map>
    #include <string>
    #include <algorithm>
    #include <type_traits>
    
    template <class T>
    struct is_pair: std::false_type { };
    
    template <class K, class V>
    struct is_pair<std::pair<K,V>>: std::true_type { };
    
    int main() {
        std::map<std::string, int> m {{"abc", 1}, {"cde", 2}, {"efg", 3}};
        std::array<int, 5> a{1, 2, 3, 4, 5};
    
        auto lambda = [](auto it) {
            if constexpr (is_pair<decltype(it)>::value) {
                return it.second == 3;
            } else {
                return it == 3;
            }
        };
    
        assert(std::find_if(a.begin(), a.end(), lambda) != a.end());
        assert(std::find_if(m.begin(), m.end(), lambda) != m.end());
    }
    

    [live demo]

    请记住,如果您决定搜索 std::vector&lt;std::pair&lt;int, int&gt;&gt; 之类的集合,则此方法将无法正常工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-18
      • 2012-02-05
      • 1970-01-01
      • 1970-01-01
      • 2022-08-24
      • 1970-01-01
      相关资源
      最近更新 更多