How to write a standard-like function that has high overload priority

In c++ 11 you could use tag dispatch. If you can make a little change to your custom iterators things will be a bit simpler to implement.

#include <iostream>
#include <algorithm>
#include <vector>
#include <type_traits>

// indicates that the type doesn't have a tag type (like pointers and standard iterators)
struct no_tag{};

namespace detail 
{
    template <typename T>
    auto tag_helper(int) -> typename T::tag;

    template <typename>
    auto tag_helper(long) -> no_tag;
}

// get T::tag or no_tag if T::tag isn't defined.
template <typename T>
using tag_t = decltype(detail::tag_helper<T>(0));

namespace N
{
    struct my_iterator_tag {};
    struct A{ using tag = my_iterator_tag; };
    struct B{ using tag = my_iterator_tag; };
    struct C{ using tag = my_iterator_tag; };
}

namespace N
{
    template<class SomeN1, class SomeN2>
    SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, no_tag)
    {
        std::cout << "calling std::copy\n";
        return std::copy(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first));
    }

    template<class SomeN1, class SomeN2>
    SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, my_iterator_tag)
    {
        // your custom copy        
        std::cout << "custom copy function\n";
        return {};
    }

    template<class SomeN1, class SomeN2>
    SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first)
    {
        return copy_helper(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first), tag_t<SomeN1>{});
    }
}

template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first)
{
    N::copy(first, second, d_first);
}

int main()
{
    N::A a1, a2, a3;
    std::cout << "using custom iterator: ";
    do_something(a1, a2, a3); 

    std::cout << "using vector iterator: ";
    std::vector<int> v;
    do_something(std::begin(v), std::end(v), std::begin(v));

    std::cout << "using pointer: ";
    int* ptr = new int[10];
    do_something(ptr, ptr + 5, ptr);

    return 0;
}

First we change our custom iterators to have a tag type (maybe change the name to avoid confusion with iterator_category). tag can be any type you want, it just has to match the type you use as tag in copy_helper.

Next, we define a type that allows us to access this tag type, or to fall back to a default type if tag doesn't exist. This will help us distinguish between our custom iterators and standard iterators and pointers. The default type I use is no_tag. The tag_t provides us with this functionality by using SFINAE and overload resolution. We call the function tag_helper(0) which has two declarations. The first one returns T::tag while the second one returns no_tag. Calling tag_helper(0) will always try to use the first version because int is a better match for 0 than long. This means we will always try to access T::tag first. However if this isn't possible (T::tag is not defined) SFINAE kicks in and skipps tag_helper(int) selecting tag_helper(long).

Finally, we just have to implement a copy function for each tag (I called it copy_helper) and another copy function as a wrap around for convinience (I used N::copy). The wrapper function then creates the proper tag type and calls the correct helper function.

Here is a live example.

Edit

If you move the code around a bit you can disconnect namespace N and rely on ADL:

#include <iostream>
#include <algorithm>
#include <vector>
#include <type_traits>

// indicates that the type doesn't have a tag type (like pointers and standard iterators)
struct no_tag{};

namespace detail 
{
    template <typename T>
    auto tag_helper(int) -> typename T::tag;

    template <typename>
    auto tag_helper(long) -> no_tag;
}

// get T::tag or no_tag if T::tag isn't defined.
template <typename T>
using tag_t = decltype(detail::tag_helper<T>(0));

namespace N
{
    struct my_iterator_tag {};
    struct A{ using tag = my_iterator_tag; };
    struct B{ using tag = my_iterator_tag; };
    struct C{ using tag = my_iterator_tag; };

    template<class SomeN1, class SomeN2>
    SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, my_iterator_tag)
    {
        // your custom copy        
        std::cout << "custom copy function\n";
        return {};
    }
}

template<class SomeN1, class SomeN2>
SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, no_tag)
{
    std::cout << "calling std::copy\n";
    return std::copy(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first));
}

template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first)
{
    copy_helper(std::forward<It1>(first), std::forward<It1>(second), std::forward<It2>(d_first), tag_t<It1>{});
}

int main()
{
    N::A a1, a2, a3;
    std::cout << "using custom iterator: ";
    do_something(a1, a2, a3); 

    std::cout << "using vector iterator: ";
    std::vector<int> v;
    do_something(std::begin(v), std::end(v), std::begin(v));

    std::cout << "using pointer: ";
    int* ptr = new int[10];
    do_something(ptr, ptr + 5, ptr);

    return 0;
}

You can declare copy() as a public friend function in your iterator classes. This works kind of as a replacement for partial specialization (which is impossible for functions), so that they will be preferred by overload resolution as they are more specialized:

#include <iostream>
#include <algorithm>
#include <vector>

namespace N
{
    template<class SomeN1, class SomeN2>
    SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first)
    {
        std::cout << "here" << std::endl;
        return d_first;
    }

    template <class T>
    struct ItBase
    {
        template <class SomeN2>
        friend SomeN2 copy(T first, T last, SomeN2 d_first)
        {
            return N::copy(first, last, d_first);
        }
    };

    struct A : ItBase<A>{};
    struct B : ItBase<B>{};
    struct C : ItBase<C>{};
}

template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first){
    using std::copy;
    copy(first, second, d_first);
}

int main(){
    N::A a1, a2, a3;
    std::cout << "do something in N:" << std::endl;
    do_something(a1, a2, a3); 

    std::vector<int> v = {1,2,3};
    std::vector<int> v2(3);
    std::cout << "do something in std:" << std::endl;
    do_something(std::begin(v), std::end(v), std::begin(v2));
    for (int i : v2)
        std::cout << i;
    std::cout << std::endl;
}

See this demo to verify that it works.

I introduced a common base class that declares the necessary friends for all of your iterators. So, instead of declaring a tag, as you tried, you just have to inherit from ItBase.

Note: If N::copy() is supposed to work with only these iterators in N, it might not be needed anymore as these friend functions will be publicly visible in N anyway (as if they were free functions).


Update:

In the comments, it has been suggested, if the iterators in N have a common base class anyway, to just declare N::copy with this base class, e.g.

namespace N
{
    template <class SomeN2>
    SomeN2 copy(ItBase first, ItBase last, SomeN2 d_first) { ... }
}

Unfortunately, this would have the opposite effect of the desired one: std::copy will always be preferred over N::copy because if you pass an instance of A, it would have to be downcasted in order to match N::copy while no cast is required for std::copy. Here you can see that obviously std::copy is tried to be called (which gives an error because N::A lacks some typedefs).

So, you cannot leverage a common base class for the signature of N::copy. The one and only reason I used one in my solution was to avoid duplicate code (having to declare the friend function in every iterator class). My ItBase does not participate in overload resolution at all.

Note, however, if your iterators happen to have some common members (whether derived from some common base class or not is not important) that you want to use in your implementation of N::copy, you can just do that with my solution above like so:

namespace N
{
    template <class T>
    struct ItBase
    {
        template <class SomeN2>
        friend SomeN2 copy(T first, T last, SomeN2 d_first)
        {
            first.some_member();
            last.some_member();
            return d_first;
        }
    };

    struct A : ItBase<A>{ void some_member() {} };
    struct B : ItBase<B>{ void some_member() {} };
    struct C : ItBase<C>{ void some_member() {} };
}

See here how it works.


On the same lines, if A, B, C have common behavior then it could be possible to replaced them by common template class parameterized in some way.

namespace N
{
    template <class T, int I>
    struct ItCommon
    {
       ...
    };
    using A = ItCommon<double,2>;
    using B = ItCommon<int, 3>;
    using C = ItCommon<char, 5>;
}
...
namespace N{
    template<class T, int I, class Other>
    SomeN2 copy(ItCommon<T, I> first, ItCommon<T, I> last, Other){
        ...
    }
} 

Since this (non-friend) copy function is definitely more constrained than the std::copy and because of ADL, it will have high priority when one of the arguments belongs to the N namespace. Also, being a non-friend, this copy function is an optional component.


This seems to fulfill your requirements:

namespace SpecCopy {

template <typename A, typename B, typename C>
void copy(A &&a, B &&b, C &&c) {
    std::copy(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c));
}

}

template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first){
    using namespace SpecCopy;
    copy(first, second, d_first);
}

Basically, it depends on ADL. If no function found by ADL, then it will use SpecCopy::copy, which is a wrapper to std::copy.


So, if you do:

N::A a1, a2, a3;
do_something(a1, a2, a3);

Then do_something will call N::copy.


If you do:

std::vector<int> a1, a2;
do_something(a1.begin(), a1.end(), a2.begin());

Then do_something will call SpecCopy::copy, which will call std::copy.


If you do:

int *a1, *a2, *a3;
do_something(a1, a2, a3);

Then same thing happens as before: do_something will call SpecCopy::copy, which will call std::copy.


One possible solution is to use another function template name and type discriminators to allow argument-dependent name lookup to find the associated function in the namespace of the arguments:

template<class T> struct Tag {};
template<class T> Tag<void> tag(T const&);

template<class It1, class It2>
void mycopy(It1 first, It1 second, It2 d_first, Tag<void>) {
    std::cout << "std::copy\n";
}

template<class It1, class It2>
void mycopy(It1 first, It1 second, It2 d_first) {
    mycopy(first, second, d_first, decltype(tag(first)){}); // Discriminate by the type of It1.
}

namespace N{

    struct itA{using trait = void;};
    Tag<itA> tag(itA);

    template<class It1, class It2>
    void mycopy(It1 first, It1 second, It2 d_first, Tag<itA>) {
        std::cout << "N::mycopy\n";
    }
}

int main() {
    char* p = 0;
    mycopy(p, p, p); // calls std::copy

    N::itA q;
    mycopy(q, q, q); // calls N::mycopy
}