unable to apply std::set_intersection on different types of structs with a common field

前端 未结 3 521
南方客
南方客 2021-01-12 14:57

I am trying to use use std::set_intersection to find common elements between 2 completely different types of data structures that have a common binding \'na

3条回答
  •  有刺的猬
    2021-01-12 15:20

    Thanks to the expressiveness of C++, there are a few ways you can solve this problem. The following is by no means an exhaustive list.

    1. Implicitly convert both types to a wrapper struct for comparison

    If you're attached to using lambdas, define a type that can be implicitly constructed from both StructA and StructB and wraps the fields used for comparison. This can allow for additional logic to be performed to the fields in the constructor before comparison. For example:

    struct Common {
        std::string const& mCommonField;
        Common(StructA const& sa) : mCommonField{sa.mCommonField} {};
        Common(StructB const& sb) : mCommonField{sb.mCommonField} {};
    };
    

    Then your comparison lambda can be written

    auto cmp = [](Common const& lhs, Common const& rhs) {
        return lhs.mCommonField < rhs.mCommonField;
    };
    

    and used like

    std::sort(aStructs.begin(), aStructs.end(), cmp);
    std::sort(bStructs.begin(), bStructs.end(), cmp);
    // ...
    std::set_intersection(aStructs.begin(), aStructs.end(),
                          bStructs.begin(), bStructs.end(),
                          std::back_inserter(intersection),
                          cmp
                          );
    

    Live example on Coliru Viewer.

    2. Use a comparator with templated operator().

    Instead of using a lambda, define a functor with a templated operator().

    struct comparator
    {
        template
        bool operator()(T const& lhs, U const& rhs) const {
            return lhs.mCommonField < rhs.mCommonField;
        }
    };
    

    Then, it's as easy as:

    std::sort(aStructs.begin(), aStructs.end(), comparator{});
    std::sort(bStructs.begin(), bStructs.end(), comparator{});
    // ...
    std::set_intersection(aStructs.begin(), aStructs.end(),
                          bStructs.begin(), bStructs.end(),
                          std::back_inserter(intersection),
                          comparator{}
                          );
    

    Just note that as there is a template in the comparator, it must be declared outside of function scope. Live example on Coliru Viewer.

    3. Wait for C++14

    And with generic lambdas added to C++14, you can use the following with a conformant compiler:

    auto cmp = [](auto lhs, auto rhs) { return lhs.mCommonField < rhs.mCommonField; };
    // ...
    std::sort(aStructs.begin(), aStructs.end(), cmp);
    std::sort(bStructs.begin(), bStructs.end(), cmp);
    // ...
    std::set_intersection(aStructs.begin(), aStructs.end(),
                          bStructs.begin(), bStructs.end(),
                          std::back_inserter(intersection),
                          cmp);
    

    Again, live example on Coliru Viewer.


    Also, C-style struct typedefs are unnecessary in C++ (and arguably unclear most places in C), so anywhere you have

    typedef struct Foo {
        // ...
    } Foo;
    

    you can replace it with

    struct Foo {
        // ...
    };
    

    without any other changes required of your code.

提交回复
热议问题