2

The C++ reference has this example for printing the intersection of two sets (or sorted containers):

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
 
int main()
{
    std::vector<int> v1{7, 2, 3, 4, 5, 6, 7, 8};
    std::vector<int> v2{5, 7, 9, 7};
    std::sort(v1.begin(), v1.end());
    std::sort(v2.begin(), v2.end());
 
    std::vector<int> v_intersection;
    std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(),
                          std::back_inserter(v_intersection));
 
    for (int n : v_intersection)
        std::cout << n << ' ';
    std::cout << '\n';
}

In my application, I simply want to iterate through the intersection of two sets; I do not need to store that intersection.

Question: What is the cleanest way to iterate through the intersection of two sets without storing the intersection?

By "cleanest" I mean without re-implementing set_intersection. Here is what I use; this "hacks" the inserter to call a function of my choosing:

#include <stack>
#include <set>
#include <vector>
#include <string>
#include <iostream>

template <typename F>
struct output_function {
    using value_type = int;
    output_function (F f) : f (f) {}
    F f;
    void push_back (int x) { f (x); }
};

int main () {
  std::vector<int> v1{7, 2, 3, 4, 5, 6, 7, 8};
  std::vector<int> v2{5, 7, 9, 7};
  std::sort(v1.begin(), v1.end());
  std::sort(v2.begin(), v2.end());

  int z = 0;
  auto f = [&z] (int t) { std::cout << z++ << ": " << t << "\n"; };
  output_function p (f);

  std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(),
                        std::back_inserter (p));
}
3
  • 1
    I would make something like an executing_iterator which would be a class type that mimics being an iterator but when you write to it, it passes the values given into a function that you pass to the iterator when you create it. Commented Apr 20 at 16:34
  • What you've got looks pretty clean to me but I'd probably make a separate function for it anyway. It's not much more code than the class you've added and it's probably clearer for a reader. Commented Apr 20 at 16:51
  • Just to show that this sort of thing can be done. Commented Apr 20 at 17:56

3 Answers 3

5

If you are willing to use Boost, you can use function_output_iterator.

It's mostly straight-forward to implement yourself, but the logic of having an assignment operator that takes an arbitrary type without hiding the copy assignment operator is a bit annoying.

Sign up to request clarification or add additional context in comments.

2 Comments

Aah, very nice! Any clue why it works well with std::set_intersection but not with std::ranges::set_intersection? godbolt.org/z/KGv9v718K
The ranges version validates the concepts, whether the set_intersection implementation uses them or not. And it requires that the output iterator be weakly_incrementable, which in turns requires movable, which requires assignable_from<T&, T>, i.e. the output iterator must be assignable. But the iterator contains a lambda which has a by-reference capture, and that's not assignable.
5

You can use range-v3's views::set_intersection:

#include <algorithm>
#include <vector>
#include <range/v3/view/set_algorithm.hpp>
 
int main()
{
    std::vector<int> v1{7, 2, 3, 4, 5, 6, 7, 8};
    std::vector<int> v2{5, 7, 9, 7};
    std::sort(v1.begin(), v1.end());
    std::sort(v2.begin(), v2.end());
 
    auto intersection = ranges::views::set_intersection(v1, v2);
    for (int n : intersection)
      // ...
}

Demo

Comments

0

This should get you most of the way there by mimicking implementation of std::insert_iterator

// TODO: Convert to class, add CTAD
template <typename T>
struct function_iterator {
    // TODO: iterator_traits stuff required by algorithms
    // TODO: Store a functor instead
    std::function<void(const T&)> fun{};

    void operator++() {}
    void operator++(int) {}
    auto& operator*() { return *this; }
    // TODO: Move overload
    auto& operator=(const T& val) { fun(val); return *this; }
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.