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

How to implement std::distance() for custom templated iterator?

本文关键字:std istance 实现 迭代器 自定义      更新时间:2023-10-16

我有一个模板化的双向迭代器。 我不想让它随机访问,因为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 calledn";
        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;
}

这是重载 std 函数(如 std::d istance)是否合理

原则上我们可以做这样的事情:

namespace std {
    template<class T>
    typename iter<T>::difference_type distance(iter<T> first, iter<T> last) {
        std::cout << "my distance calledn";
        return last - first;
    }
}

但这将是std::distance()的重载,根据标准,std命名空间函数是不允许的。

正确的

方法是在与iter-template相同的命名空间中定义distance方法(在本例中为全局命名空间)。

....
    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 calledn";
    return last - first;
}

稍后使用 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::distancen";
    std::cout << distance(begin(arr), end(arr)) << 'n';
    return 0;
}

将输出:

my distance called
5
using std::distance
5

斯科特·迈耶斯(Scott Meyers)在他的著作"有效C++",第三版,第25项中对std方法模板的部分专业化问题给出了很好的解释。