how to find the intersection of two std::set in C++?

后端 未结 5 1131
我寻月下人不归
我寻月下人不归 2020-12-04 10:43

I have been trying to find the intersection between two std::set in C++, but I keep getting an error.

I created a small sample test for this

#include         


        
5条回答
  •  借酒劲吻你
    2020-12-04 11:22

    The first (well-voted) comment of the accepted answer complains about a missing operator for the existing std set operations.

    On one hand, I understand the lack of such operators in the standard library. On the other hand, it is easy to add them (for the personal joy) if desired. I overloaded

    • operator *() for intersection of sets
    • operator +() for union of sets.

    Sample test-set-ops.cc:

    #include 
    #include 
    #include 
    
    template , class ALLOC = std::allocator >
    std::set operator * (
      const std::set &s1, const std::set &s2)
    {
      std::set s;
      std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(),
        std::inserter(s, s.begin()));
      return s;
    }
    
    template , class ALLOC = std::allocator >
    std::set operator + (
      const std::set &s1, const std::set &s2)
    {
      std::set s;
      std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(),
        std::inserter(s, s.begin()));
      return s;
    }
    
    // sample code to check them out:
    
    #include 
    
    using namespace std;
    
    template 
    ostream& operator << (ostream &out, const set &values)
    {
      const char *sep = " ";
      for (const T &value : values) {
        out << sep << value; sep = ", ";
      }
      return out;
    }
    
    int main()
    {
      set s1 { 1, 2, 3, 4 };
      cout << "s1: {" << s1 << " }" << endl;
      set s2 { 0, 1, 3, 6 };
      cout << "s2: {" << s2 << " }" << endl;
      cout << "I: {" << s1 * s2 << " }" << endl;
      cout << "U: {" << s1 + s2 << " }" << endl;
      return 0;
    }
    

    Compiled and tested:

    $ g++ -std=c++11 -o test-set-ops test-set-ops.cc 
    
    $ ./test-set-ops     
    s1: { 1, 2, 3, 4 }
    s2: { 0, 1, 3, 6 }
    I: { 1, 3 }
    U: { 0, 1, 2, 3, 4, 6 }
    
    $ 
    

    What I don't like is the copy of return values in the operators. May be, this could be solved using move assignment but this is still beyond my skills.

    Due to my limited knowledge about these "new fancy" move semantics, I was concerned about the operator returns which might cause copies of the returned sets. Olaf Dietsche pointed out that these concerns are unnecessary as std::set is already equipped with move constructor/assignment.

    Although I believed him, I was thinking how to check this out (for something like "self-convincing"). Actually, it is quite easy. As templates has to be provided in source code, you can simply step through with the debugger. Thus, I placed a break point right at the return s; of the operator *() and proceeded with single-step which leaded me immediately into std::set::set(_myt&& _Right): et voilà – the move constructor. Thanks, Olaf, for the (my) enlightment.

    For the sake of completeness, I implemented the corresponding assignment operators as well

    • operator *=() for "destructive" intersection of sets
    • operator +=() for "destructive" union of sets.

    Sample test-set-assign-ops.cc:

    #include 
    #include 
    
    template , class ALLOC = std::allocator >
    std::set& operator *= (
      std::set &s1, const std::set &s2)
    {
      auto iter1 = s1.begin();
      for (auto iter2 = s2.begin(); iter1 != s1.end() && iter2 != s2.end();) {
        if (*iter1 < *iter2) iter1 = s1.erase(iter1);
        else {
          if (!(*iter2 < *iter1)) ++iter1;
          ++iter2;
        }
      }
      while (iter1 != s1.end()) iter1 = s1.erase(iter1);
      return s1;
    }
    
    template , class ALLOC = std::allocator >
    std::set& operator += (
      std::set &s1, const std::set &s2)
    {
      s1.insert(s2.begin(), s2.end());
      return s1;
    }
    
    // sample code to check them out:
    
    #include 
    
    using namespace std;
    
    template 
    ostream& operator << (ostream &out, const set &values)
    {
      const char *sep = " ";
      for (const T &value : values) {
        out << sep << value; sep = ", ";
      }
      return out;
    }
    
    int main()
    {
      set s1 { 1, 2, 3, 4 };
      cout << "s1: {" << s1 << " }" << endl;
      set s2 { 0, 1, 3, 6 };
      cout << "s2: {" << s2 << " }" << endl;
      set s1I = s1;
      s1I *= s2;
      cout << "s1I: {" << s1I << " }" << endl;
      set s2I = s2;
      s2I *= s1;
      cout << "s2I: {" << s2I << " }" << endl;
      set s1U = s1;
      s1U += s2;
      cout << "s1U: {" << s1U << " }" << endl;
      set s2U = s2;
      s2U += s1;
      cout << "s2U: {" << s2U << " }" << endl;
      return 0;
    }
    

    Compiled and tested:

    $ g++ -std=c++11 -o test-set-assign-ops test-set-assign-ops.cc 
    
    $ ./test-set-assign-ops
    s1: { 1, 2, 3, 4 }
    s2: { 0, 1, 3, 6 }
    s1I: { 1, 3 }
    s2I: { 1, 3 }
    s1U: { 0, 1, 2, 3, 4, 6 }
    s2U: { 0, 1, 2, 3, 4, 6 }
    
    $
    

提交回复
热议问题