如何为自定义模板化迭代器实现std :: distance()?

Sza*_*lcs 12 c++ templates template-specialization

我有一个模板化的双向迭代器.我不想让它随机访问,因为it += n操作不是恒定的时间.但是,it2 - it1操作恒定的时间.我想专门std::distance()研究这个迭代器,以便使用它的算法(例如std::vector::assign())可以利用有效的差异操作.如果迭代器是模板,我该怎么做?

这是一个玩具示例:

#include <iterator>
#include <iostream>

// template bidirectional iterator
template<typename T>
class iter : public std::iterator<std::bidirectional_iterator_tag, T> {
    T *ptr;
public:
    iter(T *ptr) : ptr(ptr) { }

    iter() = default;
    iter(const iter &) = default;
    iter &operator = (const iter &) = default;

    T *operator * () { return ptr; }

    bool operator == (const iter &it) { return ptr == it.ptr; }
    bool operator != (const iter &it) { return ptr != it.ptr; }

    iter &operator ++ () { ++ptr; return *this; }
    iter operator ++ (int) { iter tmp(*this); operator++(); return tmp; }

    iter &operator -- () { --ptr; return *this; }
    iter operator -- (int) { iter tmp(*this); operator--(); return tmp; }

    // Would not be used for a bidirectional iterator.
    // Implemented only so we can use it in std::distance() below.
    ptrdiff_t operator - (const iter &it) { return ptr - it.ptr; }
};

namespace std {
    // We could specialize std::distance() for iter<int> like this:
    template<>
    iter<int>::difference_type distance(iter<int> first, iter<int> last) {
        std::cout << "my distance called\n";
        return last - first;
    }

    // QUESTION: Can we do it in general, for iter<T> ?
}

// Just to test that everything works as intended.
int main() {
    int arr[5];
    iter<int> it1(&arr[0]);
    iter<int> it2(&arr[5]);

    std::cout << std::distance(it1, it2) << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是一个后续的过程是否合理重载std函数,如std :: distance?

我们原则上可以这样做:

namespace std {
    template<class T>
    typename iter<T>::difference_type distance(iter<T> first, iter<T> last) {
        std::cout << "my distance called\n";
        return last - first;
    }
}
Run Code Online (Sandbox Code Playgroud)

但这将是一个重载std::distance(),std根据标准,名称空间函数不允许这样做.

Bo *_*o R 5

正确的方法是distance在与iter-template相同的命名空间中定义你的方法,(在这种情况下是全局命名空间)。

....
    typename iter::difference_type operator -(const iter &it)
    {
        return ptr - it.ptr;
    }
}; // close template<typename T> class iter

template<typename T>
typename iter<T>::difference_type distance( iter<T> first,  iter<T> last)
{
    std::cout << "my distance called\n";
    return last - first;
}
Run Code Online (Sandbox Code Playgroud)

稍后使用 ADL,如本例所示:

int main()
{
    int arr[5];
    iter<int> it1(&arr[0]);
    iter<int> it2(&arr[5]);

    using std::distance;
    using std::begin;
    using std::end;

    std::cout << distance(it1, it2) << '\n';
    std::cout << "using std::distance\n";
    std::cout << distance(begin(arr), end(arr)) << '\n';

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

将输出:

my distance called
5
using std::distance
5
Run Code Online (Sandbox Code Playgroud)

Scott Meyers 在他的书“Effective C++”,第三版,第 25 项中很好地解释了 std 方法模板部分特化的问题。