Does the STL provide tools that allow to do this not only for 2
but
for N
sets?
No. But you can easily make one, by providing a recursive Variadic template like as follows.
The if constexpr
part need c++17 support. However, there are many examples, how you could do it for prior to c++17. In addition, due to recursive call the argument must be passed opposite ordered, to get the behavior you were trying.
(See Online Demo)
#include <vector>
#include <algorithm> // std::set_intersection
#include <iterator> // std::back_inserter
template<typename Container, typename... Rest>
Container NSetIntersections(
const Container& container1, const Container& container2, Rest&&... rest) noexcept
{
if constexpr (sizeof...(Rest) == 0)
{
Container result;
std::set_intersection(container1.begin(), container1.end(),
container2.begin(), container2.end(), std::back_inserter(result));
return result;
}
else
{
Container result;
std::set_intersection(container1.begin(), container1.end(),
container2.begin(), container2.end(), std::back_inserter(result));
return NSetIntersections(result, std::forward<Rest>(rest)...);
}
}
int main()
{
// sorted vectors
std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
std::vector<int> v2 = { 2, 3, 4, 7, 8, 9 };
std::vector<int> v3 = { 3, 4, 7, 200 };
std::vector<int> v4 = { 4, 100, 200, 300 };
std::vector<int> v5 = { 4, 100, 200 };
// call the function like
const auto res1 = NSetIntersections(v2, v1); // 2 3 4
const auto res2 = NSetIntersections(v3, v2, v1); // 3 4
const auto res3 = NSetIntersections(v4, v3, v2, v1); // 4
const auto res4 = NSetIntersections(v5, v4, v3, v2, v1); // 4
return 0;
}
In order to pass the arguments to the NSetIntersections
function in natural way, I would suggest following helper functions manner. As a plus, it will also handle the case of passing single arguments (in case, by mistake!) to the NSetIntersections
, and c++11 compatible.
(See Online Demo)
#include <vector>
#include <algorithm> // std::set_intersection
#include <iterator> // std::back_inserter
namespace helper { // helper NSetIntersections functions
template<typename Container>
Container NSetIntersections(const Container& container1) noexcept {
return container1;
}
template<typename Container>
Container NSetIntersections(const Container& container1, const Container& container2) noexcept
{
Container result;
std::set_intersection(container1.begin(), container1.end(),
container2.begin(), container2.end(), std::back_inserter(result));
return result;
}
template<typename Container, typename... Rest>
Container NSetIntersections(
const Container& container1, const Container& container2, Rest&&... rest) noexcept
{
return helper::NSetIntersections(
helper::NSetIntersections(container1, container2), std::forward<Rest>(rest)...);
}
}
template<typename... Containers>
auto NSetIntersections(Containers&&... rest) noexcept
-> decltype(helper::NSetIntersections(std::forward<Containers>(rest)...))
{
return helper::NSetIntersections(std::forward<Containers>(rest)...);
}
Now you could call the function with args like this:
// sorted vectors
std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
std::vector<int> v2 = { 2, 3, 4, 7, 8, 9 };
std::vector<int> v3 = { 3, 4, 7, 200 };
std::vector<int> v4 = { 4, 100, 200, 300 };
std::vector<int> v5 = { 4, 100, 200 };
// call the function like
const auto res1 = NSetIntersections(v1); // 1 2 3 4 5 6
const auto res2 = NSetIntersections(v1, v2); // 2 3 4
const auto res3 = NSetIntersections(v1, v2, v3); // 3 4
const auto res4 = NSetIntersections(v1, v2, v3, v4); // 4
const auto res5 = NSetIntersections(v1, v2, v3, v4, v5); // 4
Side note: The bench mark done in quick-bench.com shows (almost) same performance (for 5 sorted containers), when we would have done N times std::set_intersection
.
(See Online Quick-bench)
std::set_intersection
twice:result = (v1 * v2) * v3
– Fouquet