4

我需要一个函数模板,它接受两个可能是指针的迭代器。如果这两个参数是 random_access 迭代器,我希望返回类型是

std::iterator<random_access_iterator_tag, ...>类型

否则一个

std::iterator<bidirectional_iterator_tag, ...>类型。

如果参数既不是双向迭代器也不是指针,我还希望代码拒绝编译。我不能依赖第三方库,例如 Boost

你能帮我处理这个函数的签名,让它接受双向迭代器和指针,但不说 input_iterator、output_iterator、forward_iterators。

我能想到的一个部分解决方案如下

template<class T>
T foo( T iter1, T iter2) {
  const T tmp1 = reverse_iterator<T>(iter1);
  const T tmp2 = reverse_iterator<T>(iter2);
  // do something
}

这个想法是,如果它不是双向的,编译器不会让我从中构造一个 reverse_iterator。

4

2 回答 2

3

这是一个enable_if基于迭代器标签的示例。如果给定的 T 没有iterator_categorytypedef 并且在重载决议期间不考虑重载,则替换失败。

由于您不能使用 C++11,请参阅参考页面enable_ifis_same了解如何自己实现它。

#include <iterator>
#include <type_traits>
#include <iostream>
#include <vector>
#include <list>

template<typename T>
typename
std::enable_if<
    std::is_same<
        typename T::iterator_category,
        std::bidirectional_iterator_tag
    >::value,
    T
>::type
foo(T it)
{
    std::cout << "bidirectional\n";
    return it;
}

template<typename T>
typename
std::enable_if<
    std::is_same<
        typename T::iterator_category,
        std::random_access_iterator_tag
    >::value,
    T
>::type
foo(T it)
{
    std::cout << "random access\n";
    return it;
}

// specialization for pointers

template<typename T>
T* foo(T* it)
{
    std::cout << "pointer\n";
    return it;
}

int main()
{
    std::list<int>::iterator it1;
    std::vector<int>::iterator it2;
    int* it3;
    std::istream_iterator<int> it4;
    foo(it1);
    foo(it2);
    foo(it3);
    //foo(it4); // this one doesn't compile, it4 is an input iterator
}

活生生的例子

根据@JonathanWakely 的评论,如果我们使用std::iterator_traits ,我们可以摆脱指针的专门化。然后该typename T::iterator_category部分变为

typename std::iterator_traits<T>::iterator_category
于 2012-11-12T20:09:51.707 回答
2

比以前的答案简单一点,不依赖于 std::enable_if:

namespace detail
{
    template<class T>
    T do_foo(T iter1, T iter2, std::random_access_iterator_tag t)
    {
        cout << "do_foo random_access" << endl;
        return iter1;
    }
    template<class T>
    T do_foo(T iter1, T iter2, std::bidirectional_iterator_tag t)
    {
        cout << "do_foo bidirectional" << endl;
        return iter1;
    }

}
template<class T>
void foo(T iter1, T iter2)
{
    typename std::iterator_traits<T>::iterator_category t;
    detail::do_foo(iter1, iter2, t);
}

int main (int argc, const char * argv[])
{
    std::vector<int> v;
    foo(v.begin(), v.end());
    std::list<int> l;
    foo(l.begin(), l.end());
    return 0;
}

该解决方案还支持从 std::random_access_iterator_tag 或 std::bidirectional_iterator_tag 派生的其他 iterator_categories(应该有),而 std::same<> 检查严格的类别相等性。

于 2012-11-13T15:42:37.987 回答