函数模板接受的不是双向迭代器或指针

时间:2012-11-12 19:23:44

标签: c++ templates stl iterator

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

的对象

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

否则

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

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

你能帮助我使用这个函数的签名,这样它就可以接受双向迭代器和指针,但不能说是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。

2 个答案:

答案 0 :(得分:3)

以下是基于迭代器标记的enable_if示例。如果给定的T没有iterator_category typedef,则替换失败,因此在重载解析期间不会考虑重载。

由于您无法使用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
}

Live example

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

typename std::iterator_traits<T>::iterator_category

答案 1 :(得分: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&lt;&gt;检查严格的类别平等。