如何在编译时检查类型是否为std :: vector :: iterator?

时间:2011-11-12 00:33:17

标签: c++ template-meta-programming

我有一个问题,我需要在编译时检测给定类型是否是已知嵌套类型的实例,例如std::vector::iterator。我想创建类型特征is_std_vector_iterator

#include <type_traits>
#include <vector>

template<typename T> struct is_std_vector_iterator : std::false_type {};

template<typename T, typename Allocator>
  struct is_std_vector_iterator<typename std::vector<T,Allocator>::iterator>
    : std::true_type
{};

int main()
{
  return 0;
}

但是我收到编译错误:

$ g++ -std=c++0x test.cpp 
test.cpp:7: error: template parameters not used in partial specialization:
test.cpp:7: error:         ‘T’
test.cpp:7: error:         ‘Allocator’

是否可以检查std::vector<T,Allocator>::iterator等依赖类型?


这是一个激发这种特质的用例:

template<typename Iterator>
Iterator my_copy(Iterator first, Iterator last, Iterator result, std::true_type)
{
  // iterators are just pointer wrappers; call memcpy
  memcpy(&*result, &*first, sizeof(typename Iterator::value_type) * last - first);
  return result + last - first;
}

template<typename Iterator>
Iterator my_copy(Iterator first, Iterator last, Iterator result, std::false_type)
{
  // use a general copy
  return std::copy(first, last, result);
}

template<typename Iterator>
Iterator my_copy(Iterator first, Iterator last, Iterator result)
{
  // dispatch based on the type of Iterator
  return my_copy(first, last, result, typename is_std_vector_iterator<Iterator1>::type())
}

3 个答案:

答案 0 :(得分:3)

嗯,在最简单的情况下,它可能看起来像这样:

#include <type_traits>
#include <vector>
#include <list>
#include <cstdio>

template <typename T>
typename std::enable_if<
    std::is_same<typename std::vector<typename T::value_type>::iterator, T>::value
    , void>::type
do_something (T begin, T end)
{
    std::printf ("Got vector iterators!\n");
}

template <typename T>
typename std::enable_if<
    !std::is_same<typename std::vector<typename T::value_type>::iterator, T>::value
    , void>::type
do_something (T begin, T end)
{
    std::printf ("Got something other than vector iterators!\n");
}

template <typename T>
typename std::enable_if<std::is_pod<T>::value, void>::type
do_something (T begin, T end)
{
    std::printf ("Got some POD iterators!\n");
}

int main()
{
    std::vector<int> ivec;
    std::list<int> ilist;
    char cdata[64];

    do_something (ivec.begin (), ivec.end ());
    do_something (ilist.begin (), ilist.end ());
    do_something (&cdata[0], cdata + 32);

    return 0;
}

但真正的问题出现在有人决定使用与默认分配器不同的分配器时。既然您想要针对一些众所周知的类型检查迭代器,而不是一个众所周知的模板,那么您基本上可以使用它,并可能使用您知道的一些分配器来扩展它。否则,用不同类型实例化的模板是一个不同的类型,我不确定是否有办法测试一个类型是否是一个专门用一些任意参数的模板实例,可能没有这种方式。

另一方面,您可以采用不同的方式解决此问题。例如,它是否是std::vector<...>迭代器有什么不同?检查它是否是随机访问等可能是有意义的。

<强>更新

对于连续布局的内存,我认为最好的办法是使用迭代器特征并检查随机访问标记。例如:

#include <type_traits>
#include <functional>
#include <vector>
#include <list>
#include <cstdio>

template <typename T>
struct is_random_access_iterator : std::is_same<
    typename std::iterator_traits<T>::iterator_category
    , std::random_access_iterator_tag>
{};

template <typename T>
typename std::enable_if<is_random_access_iterator<T>::value>::type
do_something (T begin, T end)
{
    std::printf ("Random access granted!\n");
}

template <typename T>
typename std::enable_if<!is_random_access_iterator<T>::value>::type
do_something (T begin, T end)
{
    std::printf ("No random access for us today!\n");
}

int main()
{
    std::vector<int> ivec;
    std::list<int> ilist;
    char cdata[32];

    do_something (ivec.begin (), ivec.end ());
    do_something (ilist.begin (), ilist.end ());
    do_something (&cdata[0], cdata + sizeof (cdata) / sizeof (cdata[0]));

    return 0;
}

与使用分配器检查std::vector相比,这一个肯定更简单,更加可靠。然而,即使在这种情况下,有人可以欺骗你,如果他们真的想要,购买提供随机访问迭代器,提供无缝访问不同的内存块,一旦你将其转换为指针使用指针算术而不是迭代器,你将遇到大问题重载运营商。只有在更改原始指针和迭代器时比较内存地址,才能保护自己免受攻击,但没有果汁。

希望它有所帮助。

答案 1 :(得分:1)

您应该查看来自the pretty printeris_container_helper typetrait。在该库中更精确的public version中,我调用了typetrait has_const_iterator(例如here):

template<typename T>
struct has_const_iterator
{
private:
    typedef char                      yes;
    typedef struct { char array[2]; } no;

    template<typename C> static yes test(typename C::const_iterator*);
    template<typename C> static no  test(...);
public:
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
    typedef T type;
};

答案 2 :(得分:1)

AFAIK,您可以从iterator_traits<Iter>::value_type获取迭代器的值类型。然后,您可以检查std::vector<that_value_type, Alloc>::iterator是否真的(例如boost::is_same

从您的激励示例来看,我可以看到您可能会有猜测Alloc的麻烦 - 如果您不打算使用自定义分配器,您只需将其保留为默认值。没有适用于所有Alloc的通用解决方案。