c++boostc++17boost-rangeboost-iterators

Extending iterator of boost::filtered to support operator+ overload


I would like to add a operator+ overload to boost::filter_iterator as in below example. However I am getting an error in the resolution of the template parameters to the operator+ overload function.

#include <iostream>
#include <vector>
#include <boost/range/adaptor/filtered.hpp>

 template <typename TPredicate, typename TRange> 
class Filtered_Range : public boost::filtered_range<TPredicate, TRange>
   {
     public:
  
       Filtered_Range(TPredicate Predicate, TRange& Range) : boost::filtered_range<TPredicate, TRange>(Predicate, Range) {}
  
       size_t size() const { return std::distance(this->begin(), this->end()); }
  
       auto operator[](size_t Index) const
       {
           assert(Index < this->size());
           auto It = this->begin();
           std::advance(It, Index);
           return *It;
       }
       
   };

template<typename TPredicate, typename TRange> 
typename Filtered_Range<TPredicate, TRange>::filter_iterator&  
operator+(typename 
Filtered_Range<TPredicate, TRange>::filter_iterator& f, int32_t x  ) {
    std::cout << "Custom overload\n";
    return std::advance(f, x);

}

int main() {
    const std::vector<int> nums{1, 2, 3, 4, 5, 6, 7, 8, 9};

    auto even_only_custom = Filtered_Range([] (auto n) { return (n % 2 == 0); }, nums);
    auto x = even_only_custom.begin();
    std::cout << "First Value = " << *(x) << "\n";
    std::cout << "Second Value = " << *(x + 1);  //error here
}

GCC 11.2 error message

<source>: In function 'int main()':
<source>:47:43: error: no match for 'operator+' (operand types are 'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >' and 'int')
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                         ~ ^ ~
      |                                         |   |
      |                                         |   int
      |                                         boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >
<source>:28:1: note: candidate: 'template<class TPredicate, class TRange> typename Filtered_Range<TPredicate, TRange>::filter_iterator& operator+(typename Filtered_Range<TPredicate, TRange>::filter_iterator&, int32_t)'
   28 | operator+(typename
      | ^~~~~~~~
<source>:28:1: note:   template argument deduction/substitution failed:
<source>:47:45: note:   couldn't deduce template parameter 'TPredicate'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_algobase.h:67,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/char_traits.h:39,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_iterator.h:568:5: note: candidate: 'template<class _Iterator> constexpr std::reverse_iterator<_Iterator> std::operator+(typename std::reverse_iterator<_Iterator>::difference_type, const std::reverse_iterator<_Iterator>&)'
  568 |     operator+(typename reverse_iterator<_Iterator>::difference_type __n,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_iterator.h:568:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   mismatched types 'const std::reverse_iterator<_Iterator>' and 'int'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_algobase.h:67,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/char_traits.h:39,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_iterator.h:1646:5: note: candidate: 'template<class _Iterator> constexpr std::move_iterator<_IteratorL> std::operator+(typename std::move_iterator<_IteratorL>::difference_type, const std::move_iterator<_IteratorL>&)'
 1646 |     operator+(typename move_iterator<_Iterator>::difference_type __n,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_iterator.h:1646:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   mismatched types 'const std::move_iterator<_IteratorL>' and 'int'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6094:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&, const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&)'
 6094 |     operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6094:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >' is not derived from 'const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:56,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.tcc:1169:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(const _CharT*, const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&)'
 1169 |     operator+(const _CharT* __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.tcc:1169:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   mismatched types 'const _CharT*' and 'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:56,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.tcc:1189:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(_CharT, const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&)'
 1189 |     operator+(_CharT __lhs, const basic_string<_CharT, _Traits, _Alloc>& __rhs)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.tcc:1189:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   mismatched types 'const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>' and 'int'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6131:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&, const _CharT*)'
 6131 |     operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6131:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >' is not derived from 'const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6147:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&, _CharT)'
 6147 |     operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs, _CharT __rhs)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6147:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >' is not derived from 'const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6159:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&&, const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&)'
 6159 |     operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6159:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >' is not derived from 'std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6165:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&, std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&&)'
 6165 |     operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6165:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >' is not derived from 'const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6171:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&&, std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&&)'
 6171 |     operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6171:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >' is not derived from 'std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6193:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(const _CharT*, std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&&)'
 6193 |     operator+(const _CharT* __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6193:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   mismatched types 'const _CharT*' and 'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6199:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(_CharT, std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&&)'
 6199 |     operator+(_CharT __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6199:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   mismatched types 'std::__cxx11::basic_string<_CharT, _Traits, _Allocator>' and 'int'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6205:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&&, const _CharT*)'
 6205 |     operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6205:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >' is not derived from 'std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/locale_classes.h:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/ios_base.h:41,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6211:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::__cxx11::basic_string<_CharT, _Traits, _Allocator> std::operator+(std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&&, _CharT)'
 6211 |     operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:6211:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   'boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >' is not derived from 'std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_algobase.h:67,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/char_traits.h:39,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ios:40,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_iterator.h:1242:5: note: candidate: 'template<class _Iterator, class _Container> __gnu_cxx::__normal_iterator<_Iterator, _Container> __gnu_cxx::operator+(typename __gnu_cxx::__normal_iterator<_Iterator, _Container>::difference_type, const __gnu_cxx::__normal_iterator<_Iterator, _Container>&)'
 1242 |     operator+(typename __normal_iterator<_Iterator, _Container>::difference_type
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_iterator.h:1242:5: note:   template argument deduction/substitution failed:
<source>:47:45: note:   mismatched types 'const __gnu_cxx::__normal_iterator<_Iterator, _Container>' and 'int'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
In file included from /opt/compiler-explorer/libs/boost_1_67_0/boost/range/iterator_range_core.hpp:27,
                 from /opt/compiler-explorer/libs/boost_1_67_0/boost/range/iterator_range.hpp:13,
                 from /opt/compiler-explorer/libs/boost_1_67_0/boost/range/adaptor/filtered.hpp:16,
                 from <source>:3:
/opt/compiler-explorer/libs/boost_1_67_0/boost/iterator/iterator_facade.hpp:955:3: note: candidate: 'template<class Derived, class V, class TC, class R, class D> typename boost::iterators::enable_if<boost::iterators::detail::is_traversal_at_least<TC, boost::iterators::random_access_traversal_tag>, Derived>::type boost::iterators::operator+(const boost::iterators::iterator_facade<Derived1, V1, TC1, Reference1, Difference1>&, typename Derived::difference_type)'
  955 |   BOOST_ITERATOR_FACADE_PLUS((
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/libs/boost_1_67_0/boost/iterator/iterator_facade.hpp:955:3: note:   template argument deduction/substitution failed:
/opt/compiler-explorer/libs/boost_1_67_0/boost/iterator/iterator_facade.hpp: In substitution of 'template<class Derived, class V, class TC, class R, class D> typename boost::iterators::enable_if<boost::iterators::detail::is_traversal_at_least<TC, boost::iterators::random_access_traversal_tag>, Derived>::type boost::iterators::operator+(const boost::iterators::iterator_facade<Derived1, V1, TC1, Reference1, Difference1>&, typename Derived::difference_type) [with Derived = boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > >; V = int; TC = boost::iterators::bidirectional_traversal_tag; R = const int&; D = long int]':
<source>:47:45:   required from here
/opt/compiler-explorer/libs/boost_1_67_0/boost/iterator/iterator_facade.hpp:955:3: error: no type named 'type' in 'struct boost::iterators::enable_if<boost::iterators::detail::is_traversal_at_least<boost::iterators::bidirectional_traversal_tag, boost::iterators::random_access_traversal_tag>, boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<main()::<lambda(auto:2)>, bool>, __gnu_cxx::__normal_iterator<const int*, std::vector<int> > > >'
/opt/compiler-explorer/libs/boost_1_67_0/boost/iterator/iterator_facade.hpp:960:3: note: candidate: 'template<class Derived, class V, class TC, class R, class D> typename boost::iterators::enable_if<boost::iterators::detail::is_traversal_at_least<TC, boost::iterators::random_access_traversal_tag>, Derived>::type boost::iterators::operator+(typename Derived::difference_type, const boost::iterators::iterator_facade<Derived1, V1, TC1, Reference1, Difference1>&)'
  960 |   BOOST_ITERATOR_FACADE_PLUS((
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/libs/boost_1_67_0/boost/iterator/iterator_facade.hpp:960:3: note:   template argument deduction/substitution failed:
<source>:47:45: note:   mismatched types 'const boost::iterators::iterator_facade<Derived1, V1, TC1, Reference1, Difference1>' and 'int'
   47 |     std::cout << "Second Value = " << *(x + 1);
      |                                             ^
Execution build compiler returned: 1


Any pointers on the mistake and possible solutions will be appreciated. Thanks!


Solution

  • In

    template <typename T>
        void foo(typename T::nested_type) {}
    

    T is in non-deduced context¹. This means that the only way you can get this to work is by using SFINAE.

    namespace detail {
        template <typename T> struct is_filt_it : std::false_type { };
    
        template <typename... Args>
        struct is_filt_it<boost::filter_iterator<Args...>> : std::true_type { };
    } // namespace detail
    
    template <typename It>
    static decltype(auto) operator+(It it, std::enable_if_t<detail::is_filt_it<It>::value, int32_t> x)
    {
        return std::next(it, x);
    }
    

    Now the overload always participates, but gets discarded by SFINAE for non-filter_iterator arguments:

    Live On Coliru

    #undef NDEBUG
    #include <boost/range/adaptor/filtered.hpp>
    #include <iostream>
    #include <vector>
    
    template <typename TPredicate, typename TRange>
    struct Filtered_Range : boost::filtered_range<TPredicate, TRange> {
        using base_type = boost::filtered_range<TPredicate, TRange>;
        using base_type::base_type;
    
        auto operator[](size_t i) const {
            assert(i < this->size());
            return *std::next(base_type::begin(), i);
        }
    };
    
    template <typename TPredicate, typename TRange>
    Filtered_Range(TPredicate const&, TRange const&) -> Filtered_Range<TPredicate, TRange>;
    
    namespace detail {
        template <typename T> struct is_filt_it : std::false_type { };
    
        template <typename... Args>
        struct is_filt_it<boost::filter_iterator<Args...>> : std::true_type { };
    } // namespace detail
    
    template <typename It>
    static decltype(auto) operator+(It it, std::enable_if_t<detail::is_filt_it<It>::value, int32_t> x)
    {
        return std::next(it, x);
    }
    
    int main() {
        std::vector nums{1, 2, 3, 4, 5, 6, 7, 8, 9};
    
        auto evens = Filtered_Range([](auto n) { return (n % 2 == 0); }, nums);
        auto it    = evens.begin();
        std::cout << "First  Value = " << *(it) << "\n";
        std::cout << "Second Value = " << *(it + 1) << "\n";
    }
    

    Prints

    First  Value = 2
    Second Value = 4
    

    ¹ see also e.g. What is a nondeduced context?


    Controlling Instantiations

    To limit operator+ support for the custom range subclass only, you have to make the iterator type distinguishable.

    The "tired" way would be to subclass/wrap the iterator and delegate all of the range interface to work with those instead.

    The "wired" way would be wrap the Predicate (which appears as the first template argument for the filter_iterator type!). That way we can look "inside" the filter_iterator's arguments to detect when the predicate is suitably "tagged".

    Attempt #1 (Naive)

    namespace MyLib {
        template <typename F> struct Tagged : F {
            Tagged(F f) : F(std::move(f)) {}
            using F::operator();
        };
    } // namespace MyLib
    

    Now, we decorate the TPredicate in our custom subclass:

    template <typename TPredicate, typename TRange>
    struct Filtered_Range : boost::filtered_range<MyLib::Tagged<TPredicate>, TRange> {
        using base_type = boost::filtered_range<MyLib::Tagged<TPredicate>, TRange>;
        using base_type::base_type;
    
        auto operator[](size_t i) const {
            assert(i < this->size());
            return *std::next(base_type::begin(), i);
        }
    
        using const_iterator = typename boost::range_iterator<base_type, void>::type;
    };
    

    And we extend the is_filt_it trait to check for Tagged<> predicates:

    namespace detail {
        template <typename T> struct is_tagged : std::false_type { };
        template <typename F> struct is_tagged<MyLib::Tagged<F>> : std::true_type { };
    
        template <typename T, typename = void> struct is_filt_it : std::false_type { };
    
        template <typename F, typename... Args>
        struct is_filt_it<boost::filter_iterator<F, Args...>,
                          std::enable_if_t<is_tagged<F>::value>> : std::true_type {
        };
    } // namespace detail
    

    Sadly, this breaks:

    Candidate template ignored: requirement 'detail::is_filt_it<boost::iterators::filter_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<MyLib::Tagged<...>, bool>...

    As you can see, Boost already had a wrapper (at least sometimes) and it breaks our detection

    Attempt #2 (Galaxy Brain)

    So do we have to get tedious and change the actual iterator type?

    No! There's a feature in C++ that is sometimes a little poisonous: Argument dependent lookup. ADL is designed to bring in "associated namespaces" for lookup. Turns out namespaces that declare the types named in template arguments (and their template arguments) are considered "associated".

    So:

    namespace MyLib {
        namespace AdlBarrier {
            template <typename F> struct Tagged : F {
                Tagged(F f) : F(std::move(f)) {}
                using F::operator();
            };
    
            template <typename T> constexpr bool is_tagged(T&&) { return true; }
        } // namespace AdlBarrier
    
        using AdlBarrier::Tagged;
    } // namespace MyLib
    

    Nothing changes about Filtered_Range, and:

    namespace detail {
        template <typename T, typename = void> struct is_filt_it : std::false_type { };
    
        template <typename F, typename... Args>
        struct is_filt_it<boost::filter_iterator<F, Args...>,
                          std::enable_if_t<is_tagged(static_cast<F*>(nullptr))>>
            : std::true_type {
        };
    } // namespace detail
    

    Now, you have special treatment for iterators from your custom range:

    {
        auto evens = Filtered_Range([](auto n) { return (n % 2 == 0); }, nums);
        auto it    = evens.begin();
        std::cout << "First  Value = " << *(it) << "\n";
        std::cout << "Second Value = " << *(it + 1) << "\n";
    }
    

    But plain vanilla boost::filtered_range doesn't get special treatment:

    {
        auto odds = boost::filtered_range([](auto n) { return (n % 2 == 1); }, nums);
        auto it   = odds.begin();
        std::cout << "First  Value = " << *(it) << "\n";
        std::cout << "Second Value = " << *(it + 1) << "\n"; // doesn't compile
    }
    

    See it Live: https://godbolt.org/z/xhqjqa3Gc (or correctly not-compiling: https://godbolt.org/z/67vP6TEGc)