简体   繁体   English

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

[英]How to check whether a type is std::vector::iterator at compile time?

I have a problem where I need to detect whether a given type is an instance of a known nested type such as std::vector::iterator at compile time. 我有一个问题,我需要在编译时检测给定类型是否是已知嵌套类型的实例,如std::vector::iterator I'd like to create the type trait is_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;
}

But I receive the compiler error: 但我收到编译器错误:

$ 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’

Is it possible to check for a dependent type like std::vector<T,Allocator>::iterator ? 是否可以检查依赖类型,如std::vector<T,Allocator>::iterator


Here's a motivating use case of such a trait: 这是一个激发这种特质的用例:

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())
}

Well, in the simplest case scenario it could look something like this: 那么,在最简单的情况下,它可能看起来像这样:

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

But the real problem comes when someone decides to use allocator different from default one. 但真正的问题出现在有人决定使用与默认分配器不同的分配器时。 Since you want to check iterator against some well known type, not a well known template, then you can basically use this and possibly extend it with some allocators that you are aware of. 既然您想要针对一些众所周知的类型检查迭代器,而不是一个众所周知的模板,那么您基本上可以使用它,并可能使用您知道的一些分配器来扩展它。 Otherwise, a template instantiated with different types is a different type, and I am not sure if there is a way to test if a type is an instance of template specialized with some arbitrary parameter, there is probably no such way. 否则,使用不同类型实例化的模板是不同的类型,我不确定是否有办法测试类型是否是专用于某个任意参数的模板实例,可能没有这种方法。

On the other hand, you may solve this problem differently. 另一方面,您可以以不同方式解决此问题。 For example, what difference it makes whether this is std::vector<...> iterator or not? 例如,它是否与std::vector<...>迭代器有何不同? It might make sense to check whether it is random access or not, etc. 检查它是否是随机访问等可能是有意义的。

UPDATE: 更新:

For continuously laid out memory, I'd say the best bet is to use iterator traits and check for random access tag. 对于连续布局的内存,我认为最好的办法是使用迭代器特征并检查随机访问标记。 For example: 例如:

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

This one will be definitely simpler and even more solid than checking against std::vector with allocators. 这个将比使用allocator检查std::vector更简单,更加可靠。 However, even in this case someone can fool you if they really want, buy providing you random access iterator that provides seamless access to different chunks of memory, and you will have big problems once you convert that into a pointer use pointer arithmetics rather than iterator's overloaded operators. 然而,即使在这种情况下,有人可以欺骗你,如果他们真的想要,购买提供随机访问迭代器,提供无缝访问不同的内存块,一旦你将其转换为指针使用指针算术而不是迭代器,你将遇到大问题重载运营商。 You can protect yourself against that only by comparing memory addresses while changing both raw pointer and iterator, but there is no juice. 只有在更改原始指针和迭代器时比较内存地址,才能保护自己免受攻击,但是没有果汁。

Hope it helps. 希望能帮助到你。

You should look at the is_container_helper typetrait from the pretty printer . 您应该从漂亮的打印机查看is_container_helper typetrait。 In the more refined public version of that library, I call the typetrait has_const_iterator (eg here ): 在该库的更精致的公共版本中,我调用了typetrait has_const_iterator (例如这里 ):

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

AFAIK, you can get the value type of an iterator from iterator_traits<Iter>::value_type . AFAIK,您可以从iterator_traits<Iter>::value_type获取iterator_traits<Iter>::value_type的值类型。 Then, you can check that std::vector<that_value_type, Alloc>::iterator is really it (eg. with boost::is_same ) 然后,您可以检查std::vector<that_value_type, Alloc>::iterator是否真的如此(例如,使用boost::is_same

BTW from your motivating example, I can see you'll probably have troubles guessing Alloc - if you don't plan on using custom allocators, you'll just leave it as default. 从您的激励示例来看,我可以看到您可能会有猜测Alloc麻烦 - 如果您不打算使用自定义分配器,您只需将其保留为默认值。 There is no general solution that will work on all Alloc s. 没有适用于所有Alloc的通用解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM