3

I'm trying to write an operator| for my template class boo and everything works out fine until the template class is a boost range type - like in the example a boost::range::filter_range - adl prefers the boost::range_detail::operator|(SinglePassRange& r, const replace_holder<T>) over the local one.

Can anyone explain why adl is preferring the overload from the boost this detailed namespace over the local namespace?

#include <vector>
#include <boost/range/adaptors.hpp>

namespace local
{
    template<typename T>
    struct boo {};

    // this overload is not prefered when T is a boost::range::xxx_range
    template<typename T, typename U>
    auto operator|(boo<T>, U)
    {
        return false;
    }

    void finds_local_operator_overload()
    {
        std::vector<int> xs;

        // works like expected and calls local::operator|
        auto f = boo<decltype(xs)>{} | xs;
    }

    void prefers_boost_range_detail_replaced_operator_overload_instead_of_local_operator()
    {
        std::vector<int> xs;
        // compiler error because it tries to call 'boost::range_detail::operator|'
        auto filtered = xs | boost::adaptors::filtered([](auto &&x){ return x % 2; });
        auto f = boo<decltype(filtered)>{} | xs;
    }

}

clang error (msvc reports almost the same):

/xxx/../../thirdparty/boost/1.60.0/dist/boost/range/value_type.hpp:26:70: error: no type named 'type' in
      'boost::range_iterator<local::boo<boost::range_detail::filtered_range<(lambda at
      /xxx/Tests.cpp:221:49), std::vector<int, std::allocator<int> > > >, void>'
    struct range_value : iterator_value< typename range_iterator<T>::type >
                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/xxx/../../thirdparty/boost/1.60.0/dist/boost/range/adaptor/replaced.hpp:122:40: note: in instantiation of template class
      'boost::range_value<local::boo<boost::range_detail::filtered_range<(lambda at
      /xxx/Tests.cpp:221:49), std::vector<int, std::allocator<int> > > > >' requested
      here
               BOOST_DEDUCED_TYPENAME range_value<SinglePassRange>::type>& f)
                                       ^
/xxx/Tests.cpp:222:37: note: while substituting deduced template arguments into
      function template 'operator|' [with SinglePassRange = local::boo<boost::range_detail::filtered_range<(lambda at
      /xxx/Tests.cpp:221:49), std::vector<int, std::allocator<int> > > >]
        auto f = boo<decltype(filtered)>{} | xs;
Cœur
  • 37,241
  • 25
  • 195
  • 267
TimW
  • 8,351
  • 1
  • 29
  • 33
  • 1
    Both operator should be considered. but I suppose the boost's one is more specialized that your's. – Jarod42 Jan 11 '17 at 15:43
  • Why is the `boost::range_detail::replace_holder` template functions considered? There is no argument that opens the boost namespace. There is only the template 'T' that is from the boost namespace . And if there is a good reason for considering it, why aren't the other boost range holder template functions not considered? – TimW Jan 11 '17 at 17:49

1 Answers1

2

According to the rules of ADL, namespace and classes added in the set for overloads for boo<decltype(filtered)>{} | xs are local (for boo), boost::range_detail (for decltype(filtered)) and std (for std::vector<int> xs).

We have in particularity:

(As you expect, your's in local

template<typename T, typename U> auto operator|(boo<T>, U);

and )

a problematic one in boost::range_detail:

template <class SinglePassRange>
replaced_range<const SinglePassRange>
operator|(
    const SinglePassRange&,
    const replace_holder<typename range_value<SinglePassRange>::type>&);

So we have the non deduced range_value<boo<decltype(filtered)>>::type which provokes a hard error. (The method is unfortunately not SFINAE friendly to be removed from overload set).

Error happens before overload_resolution.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • There seems to be some ambiguity in the cppreference documented 'class template specialization': in this case, `boo` is clearly not a specialization, it is a class template _instantiation_. – xtofl Jan 12 '17 at 08:20
  • 2
    @xtofl it's a specialization (well, *simple-template-id* that names a specailization), its use in a program may cause an (implicit) instantiation as per 14.7.1[temp.inst] – Cubbi Jan 22 '17 at 19:24