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

时间:2017-11-06 10:04:26

标签: 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;
}

这是Is it reasonable to overload std functions such as 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;
    }
}

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

1 个答案:

答案 0 :(得分:3)

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

....
    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;
}

以后使用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;
}

将输出:

my distance called
5
using std::distance
5

Scott Meyers在其着作“Effective C ++”第三版第25项中给出了std方法模板部分特化问题的一个很好的解释。