【问题标题】:Function template accepting nothing less than a bidirectional iterator or a pointer函数模板只接受双向迭代器或指针
【发布时间】:2012-11-12 19:23:44
【问题描述】:

我需要一个函数模板,它接受两个可能是指针的迭代器。如果这两个参数是 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。

【问题讨论】:

  • @PaulManta 是的,它必须基于 iterator_tags,但我似乎无法得到一个干净的解决方案。
  • 不幸的是,C++ 元编程很少是干净的。你的编译器支持enable_if吗?
  • @PaulManta 是的,你是对的,我对模板很陌生。我得检查一下。我在 gcc 4.5 上,但代码需要合理“便携”和 C++-03

标签: c++ templates stl iterator


【解决方案1】:

这是一个基于迭代器标签的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

【讨论】:

  • 谢谢。我会看看是否有一种快速的方法来推出我自己的 enable_if。
  • 为什么不使用std::iterator_traits&lt;T&gt;::iterator_category 并免费获得指针支持?迭代器不需要定义嵌套的iterator_category,它们可以专门化iterator_traits
  • @JonathanWakely 我不知道 iterator_traits 专门用于指针,很好。将其添加到答案中。
【解决方案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 检查严格的类别相等性。

【讨论】:

    猜你喜欢
    • 2021-09-03
    • 2021-12-26
    • 2018-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多