c++c++20projectionstd-rangesset-intersection

error compiling ranges::set_intersection with projection


The code:

#include <algorithm>
#include <ranges>
#include <vector> 
#include <tuple>
#include <generator>
#include <cstdio>
#include <iostream>
using namespace std;
 
struct Edge{int from, to;auto operator<=>(const Edge&) const =default;};
enum NeighborDegree{FIRST_NEIGHBOR_DEGREE,SECOND_NEIGHBOR_DEGREE,THIRD_NEIGHBOR_DEGREE};
 
int main()
{
    const vector<Edge> links;
    vector<tuple<Edge,NeighborDegree> > multi_degree_neighbors, inter;
    ranges::set_intersection(multi_degree_neighbors, multi_degree_neighbors, back_inserter(inter), {}, {}, {});
    auto prj1=[](const auto& neighbor)->Edge{const auto& [e,degree]=neighbor;return e;};
    ranges::set_intersection(multi_degree_neighbors, links, back_inserter(inter), {}, prj1, {});
}

The error:

prog.cc: In function 'int main()':
prog.cc:17:29: error: no match for call to '(const std::ranges::__set_intersection_fn) (std::vector<std::tuple<Edge, NeighborDegree> >&, std::vector<std::tuple<Edge, NeighborDegree> >&, std::back_insert_iterator<std::vector<std::tuple<Edge, NeighborDegree> > >, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>)'
   17 |     ranges::set_intersection(multi_degree_neighbors, multi_degree_neighbors, back_inserter(inter), {}, {}, {});
      |     ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/wandbox/gcc-head/include/c++/16.0.0/algorithm:65,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2751:10: note: there are 2 candidates
 2751 |   struct __set_intersection_fn
      |          ^~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2759:7: note: candidate 1: 'template<class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Out, class _Comp, class _Proj1, class _Proj2>  requires (input_iterator<_Iter1>) && (sentinel_for<_Sent1, _Iter1>) && (input_iterator<_Iter2>) && (sentinel_for<_Sent2, _Iter2>) && (weakly_incrementable<_Out>) && (mergeable<_Iter1, _Iter2, _Out, _Comp, _Proj1, _Proj2>) constexpr std::ranges::set_intersection_result<_Iter1, _Iter2, _Out> std::ranges::__set_intersection_fn::operator()(_Iter1, _Sent1, _Iter2, _Sent2, _Out, _Comp, _Proj1, _Proj2) const'
 2759 |       operator()(_Iter1 __first1, _Sent1 __last1,
      |       ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2759:7: note: template argument deduction/substitution failed:
prog.cc:17:29: note:   couldn't deduce template parameter '_Sent2'
   17 |     ranges::set_intersection(multi_degree_neighbors, multi_degree_neighbors, back_inserter(inter), {}, {}, {});
      |     ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2793:7: note: candidate 2: 'template<class _Range1, class _Range2, class _Out, class _Comp, class _Proj1, class _Proj2>  requires (input_range<_Range1>) && (input_range<_Range2>) && (weakly_incrementable<_Out>) && (mergeable<decltype(std::ranges::__access::__begin((declval<_Container&>)())), decltype(std::ranges::__access::__begin((declval<_Range2&>)())), _Out, _Comp, _Proj1, _Proj2>) constexpr std::ranges::set_intersection_result<std::ranges::borrowed_iterator_t<_Range>, std::ranges::borrowed_iterator_t<_Range2>, _Out> std::ranges::__set_intersection_fn::operator()(_Range1&&, _Range2&&, _Out, _Comp, _Proj1, _Proj2) const'
 2793 |       operator()(_Range1&& __r1, _Range2&& __r2, _Out __result,
      |       ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2793:7: note: template argument deduction/substitution failed:
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2793:7: note: constraints not satisfied
In file included from /opt/wandbox/gcc-head/include/c++/16.0.0/compare:42,
                 from /opt/wandbox/gcc-head/include/c++/16.0.0/bits/stl_pair.h:65,
                 from /opt/wandbox/gcc-head/include/c++/16.0.0/bits/stl_algobase.h:64,
                 from /opt/wandbox/gcc-head/include/c++/16.0.0/algorithm:62:
/opt/wandbox/gcc-head/include/c++/16.0.0/concepts: In substitution of 'template<class _Range1, class _Range2, class _Out, class _Comp, class _Proj1, class _Proj2>  requires (input_range<_Range1>) && (input_range<_Range2>) && (weakly_incrementable<_Out>) && (mergeable<decltype(std::ranges::__access::__begin((declval<_Container&>)())), decltype(std::ranges::__access::__begin((declval<_Range2&>)())), _Out, _Comp, _Proj1, _Proj2>) constexpr std::ranges::set_intersection_result<std::ranges::borrowed_iterator_t<_Range>, std::ranges::borrowed_iterator_t<_Range2>, _Out> std::ranges::__set_intersection_fn::operator()(_Range1&&, _Range2&&, _Out, _Comp, _Proj1, _Proj2) const [with _Range1 = std::vector<std::tuple<Edge, NeighborDegree> >&; _Range2 = std::vector<std::tuple<Edge, NeighborDegree> >&; _Out = std::back_insert_iterator<std::vector<std::tuple<Edge, NeighborDegree> > >; _Comp = std::ranges::less; _Proj1 = std::identity; _Proj2 = std::identity]':
prog.cc:17:29:   required from here
   17 |     ranges::set_intersection(multi_degree_neighbors, multi_degree_neighbors, back_inserter(inter), {}, {}, {});
      |     ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/concepts:362:13:   required for the satisfaction of 'invocable<_Fn, _Args ...>' [with _Fn = std::ranges::less&; _Args = {std::tuple<Edge, NeighborDegree>&, std::tuple<Edge, NeighborDegree>&}]
/opt/wandbox/gcc-head/include/c++/16.0.0/concepts:366:13:   required for the satisfaction of 'regular_invocable<_Fn, _Args ...>' [with _Fn = std::ranges::less&; _Args = {std::tuple<Edge, NeighborDegree>&, std::tuple<Edge, NeighborDegree>&}]
/opt/wandbox/gcc-head/include/c++/16.0.0/concepts:370:13:   required for the satisfaction of 'predicate<_Rel, _Tp, _Tp>' [with _Rel = std::ranges::less&; _Tp = std::tuple<Edge, NeighborDegree>&]
/opt/wandbox/gcc-head/include/c++/16.0.0/concepts:375:13:   required for the satisfaction of 'relation<_Rel, _Tp, _Up>' [with _Rel = std::ranges::less&; _Tp = std::tuple<Edge, NeighborDegree>&; _Up = std::tuple<Edge, NeighborDegree>&]
/opt/wandbox/gcc-head/include/c++/16.0.0/concepts:385:13:   required for the satisfaction of 'strict_weak_order<_Fn&, typename std::__detail::__indirect_value<_Iter>::type, typename std::__detail::__indirect_value<_I2>::type>' [with _Fn = std::ranges::less; _I1 = std::__detail::__projected<__gnu_cxx::__normal_iterator<std::tuple<Edge, NeighborDegree>*, std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > > >, std::identity>::__type; _I2 = std::__detail::__projected<__gnu_cxx::__normal_iterator<std::tuple<Edge, NeighborDegree>*, std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > > >, std::identity>::__type]
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:789:13:   required for the satisfaction of 'indirect_strict_weak_order<_Rel, std::projected<_I1, _P1>, std::projected<_I2, _P2> >' [with _Rel = std::ranges::less; _I1 = __gnu_cxx::__normal_iterator<std::tuple<Edge, NeighborDegree>*, std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > > >; _P1 = std::identity; _I2 = __gnu_cxx::__normal_iterator<std::tuple<Edge, NeighborDegree>*, std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > > >; _P2 = std::identity]
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:998:13:   required for the satisfaction of 'mergeable<decltype (std::ranges::__access::__begin(declval<_Container&>())), decltype (std::ranges::__access::__begin(declval<_Range2&>())), _Out, _Comp, _Proj1, _Proj2>' [with _Range1 = std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > >&; _Range2 = std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > >&; _Out = std::back_insert_iterator<std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > > >; _Comp = std::ranges::less; _Proj1 = std::identity; _Proj2 = std::identity]
/opt/wandbox/gcc-head/include/c++/16.0.0/concepts:362:25: note: the expression 'is_invocable_v<_Fn, _Args ...> [with _Fn = std::ranges::less&; _Args = {std::tuple<Edge, NeighborDegree>&, std::tuple<Edge, NeighborDegree>&}]' evaluated to 'false'
  362 |     concept invocable = is_invocable_v<_Fn, _Args...>;
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.cc:19:29: error: no match for call to '(const std::ranges::__set_intersection_fn) (std::vector<std::tuple<Edge, NeighborDegree> >&, const std::vector<Edge>&, std::back_insert_iterator<std::vector<std::tuple<Edge, NeighborDegree> > >, <brace-enclosed initializer list>, main()::<lambda(const auto:58&)>&, <brace-enclosed initializer list>)'
   19 |     ranges::set_intersection(multi_degree_neighbors, links, back_inserter(inter), {}, prj1, {});
      |     ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2751:10: note: there are 2 candidates
 2751 |   struct __set_intersection_fn
      |          ^~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2759:7: note: candidate 1: 'template<class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Out, class _Comp, class _Proj1, class _Proj2>  requires (input_iterator<_Iter1>) && (sentinel_for<_Sent1, _Iter1>) && (input_iterator<_Iter2>) && (sentinel_for<_Sent2, _Iter2>) && (weakly_incrementable<_Out>) && (mergeable<_Iter1, _Iter2, _Out, _Comp, _Proj1, _Proj2>) constexpr std::ranges::set_intersection_result<_Iter1, _Iter2, _Out> std::ranges::__set_intersection_fn::operator()(_Iter1, _Sent1, _Iter2, _Sent2, _Out, _Comp, _Proj1, _Proj2) const'
 2759 |       operator()(_Iter1 __first1, _Sent1 __last1,
      |       ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2759:7: note: template argument deduction/substitution failed:
prog.cc:19:29: note:   couldn't deduce template parameter '_Sent2'
   19 |     ranges::set_intersection(multi_degree_neighbors, links, back_inserter(inter), {}, prj1, {});
      |     ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2793:7: note: candidate 2: 'template<class _Range1, class _Range2, class _Out, class _Comp, class _Proj1, class _Proj2>  requires (input_range<_Range1>) && (input_range<_Range2>) && (weakly_incrementable<_Out>) && (mergeable<decltype(std::ranges::__access::__begin((declval<_Container&>)())), decltype(std::ranges::__access::__begin((declval<_Range2&>)())), _Out, _Comp, _Proj1, _Proj2>) constexpr std::ranges::set_intersection_result<std::ranges::borrowed_iterator_t<_Range>, std::ranges::borrowed_iterator_t<_Range2>, _Out> std::ranges::__set_intersection_fn::operator()(_Range1&&, _Range2&&, _Out, _Comp, _Proj1, _Proj2) const'
 2793 |       operator()(_Range1&& __r1, _Range2&& __r2, _Out __result,
      |       ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2793:7: note: template argument deduction/substitution failed:
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/ranges_algo.h:2793:7: note: constraints not satisfied
In file included from /opt/wandbox/gcc-head/include/c++/16.0.0/bits/stl_iterator_base_types.h:73,
                 from /opt/wandbox/gcc-head/include/c++/16.0.0/bits/stl_algobase.h:65:
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h: In substitution of 'template<class _Range1, class _Range2, class _Out, class _Comp, class _Proj1, class _Proj2>  requires (input_range<_Range1>) && (input_range<_Range2>) && (weakly_incrementable<_Out>) && (mergeable<decltype(std::ranges::__access::__begin((declval<_Container&>)())), decltype(std::ranges::__access::__begin((declval<_Range2&>)())), _Out, _Comp, _Proj1, _Proj2>) constexpr std::ranges::set_intersection_result<std::ranges::borrowed_iterator_t<_Range>, std::ranges::borrowed_iterator_t<_Range2>, _Out> std::ranges::__set_intersection_fn::operator()(_Range1&&, _Range2&&, _Out, _Comp, _Proj1, _Proj2) const [with _Range1 = std::vector<std::tuple<Edge, NeighborDegree> >&; _Range2 = const std::vector<Edge>&; _Out = std::back_insert_iterator<std::vector<std::tuple<Edge, NeighborDegree> > >; _Comp = std::ranges::less; _Proj1 = main()::<lambda(const auto:58&)>; _Proj2 = std::identity]':
prog.cc:19:29:   required from here
   19 |     ranges::set_intersection(multi_degree_neighbors, links, back_inserter(inter), {}, prj1, {});
      |     ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:599:13:   required for the satisfaction of 'indirectly_writable<_Out, std::iter_reference_t<_Iter> >' [with _Out = std::back_insert_iterator<std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > > >; _In = __gnu_cxx::__normal_iterator<const Edge*, std::vector<Edge, std::allocator<Edge> > >]
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:874:13:   required for the satisfaction of 'indirectly_copyable<_I2, _Out>' [with _I2 = __gnu_cxx::__normal_iterator<const Edge*, std::vector<Edge, std::allocator<Edge> > >; _Out = std::back_insert_iterator<std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > > >]
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:998:13:   required for the satisfaction of 'mergeable<decltype (std::ranges::__access::__begin(declval<_Container&>())), decltype (std::ranges::__access::__begin(declval<_Range2&>())), _Out, _Comp, _Proj1, _Proj2>' [with _Range1 = std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > >&; _Range2 = const std::vector<Edge, std::allocator<Edge> >&; _Out = std::back_insert_iterator<std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > > >; _Comp = std::ranges::less; _Proj1 = main::._anon_221; _Proj2 = std::identity]
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:599:35:   in requirements with '_Out&& __o', '_Tp&& __t' [with _Out = std::back_insert_iterator<std::vector<std::tuple<Edge, NeighborDegree>, std::allocator<std::tuple<Edge, NeighborDegree> > > >; _Tp = const Edge&]
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:601:14: note: the required expression '*__o =(forward<_Tp>)(__t)' is invalid
  601 |         *__o = std::forward<_Tp>(__t);
      |         ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:602:34: note: the required expression '*(forward<_Out>)(__o)=(forward<_Tp>)(__t)' is invalid
  602 |         *std::forward<_Out>(__o) = std::forward<_Tp>(__t);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:604:11: note: the required expression 'const_cast<const decltype(*(declval<_Iter&>)())&&>(*__o) =(forward<_Tp>)(__t)' is invalid
  603 |         const_cast<const iter_reference_t<_Out>&&>(*__o)
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  604 |           = std::forward<_Tp>(__t);
      |           ^~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/gcc-head/include/c++/16.0.0/bits/iterator_concepts.h:606:11: note: the required expression 'const_cast<const decltype(*(declval<_Iter&>)())&&>(*(forward<_Out>)(__o))=(forward<_Tp>)(__t)' is invalid
  605 |         const_cast<const iter_reference_t<_Out>&&>(*std::forward<_Out>(__o))
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  606 |           = std::forward<_Tp>(__t);
      |           ^~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail

Solution

  • This fails because std::ranges::set_intersection requires the ranges be std::mergeable, which requires elements from both input ranges be copyable into the output range. This is arguably an overconstraint for intersection (and difference), but it is required for union and symmetric difference. I imagine it was specified this way to be consistent among all these operations.

    The reason one might not consider this an overconstraint is that mathematically, it shouldn't matter which input the elements are copied from, when doing set operations.