我想将std::iterator_traits<>
专门用于容器类模板的迭代器,该模板使不具有通常的嵌套typedef(如value_type
,difference_type
,等)我的源不应该修改。基本上我想做这样的事情:
template <typename T> struct iterator_traits<typename Container<T>::iterator>
{
typedef T value_type;
// etc.
};
除了这不起作用,因为编译器无法从T
推断出Container<T>::iterator
。
有没有办法实现同样的目标?
例如:
template <typename T>
class SomeContainerFromAThirdPartyLib
{
typedef T ValueType; // not value_type!
// no difference_type
class iterator
{
typedef T ValueType; // not value_type!
// no difference_type
...
};
iterator begin() { ... }
iterator end() { ... }
...
};
现在假设我使用此类的实例调用std::count()
。据我所知,在大多数STL实现中,count()
返回iterator_traits<Iterator>::difference_type
。 iterator_traits<I>
的主要模板仅执行typedef typename I::difference_type difference_type
。与其他嵌套类型相同。
现在在我们的例子中,这显然不起作用,因为没有Container::iterator::difference_type
。我认为我可以在不修改迭代器类的情况下解决这个问题,方法是将iterator_traits
专门用于任何Container<T>
的迭代器。
最后,我只想使用std算法,如count,find,sort等,最好不要修改任何现有代码。我认为iterator_traits
的重点正是:能够为不支持内置类型的迭代器类型指定类型(如value_type
,diff_type
等)。不幸的是,我无法弄清楚如何为Container<T>
的所有实例专门化traits类。
答案 0 :(得分:10)
是。编译器无法从T
推导出Container<T>::iterator
因为它是不可导入的上下文,换句话说,在给定Container<T>::iterator
的情况下,T
的值不能唯一且可靠地推导出来(见this for detail explanation)。
此问题的唯一解决方案是,您要为您打算在程序中使用的iterator_traits
的每个可能值完全专门化iterator
。没有通用解决方案,因为您不允许编辑Container<T>
类模板。
答案 1 :(得分:4)
Nawaz's answer可能是正确的解决方案。但是,如果您尝试为许多实例化的SomeContainerFromAThirdPartyLib<T>
类和仅少数函数(或未知数量的实例化但是固定数量的函数,如果您正在编写自己的库时可能会发生这种情况)执行此操作,还有另一种方式。
假设我们获得了以下(不可更改的)代码:
namespace ThirdPartyLib
{
template <typename T>
class SomeContainerFromAThirdPartyLib
{
public:
typedef T ValueType; // not value_type!
// no difference_type
class iterator
{
public:
typedef T ValueType; // not value_type!
// no difference_type
// obviously this is not how these would actually be implemented
int operator != (const iterator& rhs) { return 0; }
iterator& operator ++ () { return *this; }
T operator * () { return T(); }
};
// obviously this is not how these would actually be implemented
iterator begin() { return iterator(); }
iterator end() { return iterator(); }
};
}
我们定义了一个适配器类模板,其中包含typedef
所需的iterator_traits
s并专门用于避免指针出现问题:
namespace MyLib
{
template <typename T>
class iterator_adapter : public T
{
public:
// replace the following with the appropriate types for the third party iterator
typedef typename T::ValueType value_type;
typedef std::ptrdiff_t difference_type;
typedef typename T::ValueType* pointer;
typedef typename T::ValueType& reference;
typedef std::input_iterator_tag iterator_category;
explicit iterator_adapter(T t) : T(t) {}
};
template <typename T>
class iterator_adapter<T*>
{
};
}
然后,对于我们希望能够使用SomeContainerFromAThirdPartyLib::iterator
调用的每个函数,我们定义一个重载并使用SFINAE:
template <typename iter>
typename MyLib::iterator_adapter<iter>::difference_type
count(iter begin, iter end, const typename iter::ValueType& val)
{
cout << "[in adapter version of count]";
return std::count(MyLib::iterator_adapter<iter>(begin), MyLib::iterator_adapter<iter>(end), val);
}
然后我们可以按如下方式使用它:
int main()
{
char a[] = "Hello, world";
cout << "a=" << a << endl;
cout << "count(a, a + sizeof(a), 'l')=" << count(a, a + sizeof(a), 'l') << endl;
ThirdPartyLib::SomeContainerFromAThirdPartyLib<int> container;
cout << "count(container.begin(), container.end(), 0)=";
cout << count(container.begin(), container.end(), 0) << std;
return 0;
}
您可以在http://ideone.com/gJyGxU找到包含所需include
和using
s的可运行示例。输出:
a=Hello, world count(a, a + sizeof(a), 'l')=3 count(container.begin(), container.end(), 0)=[in adapter version of count]0
不幸的是,有一些警告:
find
,sort
等等。这显然不适用于algorithm
中尚未定义的函数。关于最后一个,问题在于放置重载的命名空间(以及如何调用std
版本)。理想情况下,它会在ThirdPartyLib
中,以便可以通过参数依赖查找找到它,但我认为我们无法改变它。下一个最佳选项位于MyLib
,但是调用必须是合格的,或者前面有using
。在任何一种情况下,最终用户都应使用using std::count;
或注意哪些来电符合std::
,因为如果std::count
错误地与SomeContainerFromAThirdPartyLib::iterator
一起使用,那么很明显失败(这个练习的全部原因)。
我不建议的替代方案,但在此处提供完整性将是将其直接放在std
命名空间中。这会导致未定义的行为;虽然它可能对你有用,但标准中没有任何东西可以保证它。如果我们专门化count
而不是重载它,这将是合法的。
答案 2 :(得分:1)
在有问题的专业化中,T
处于不可约的上下文中,但既没有第三方库容器代码更改,也没有std
命名空间中的任何特化。
如果第三方库没有在相应的命名空间中提供任何免费begin
和end
函数,则可以编写自己的函数(如果需要启用ADL,则进入该命名空间)并将迭代器包装到自己的包装类,它反过来提供必要的typedef和运算符。
第一个需要Iterator包装器。
#include <cstddef>
namespace ThirdPartyStdAdaptor
{
template<class Iterator>
struct iterator_wrapper
{
Iterator m_it;
iterator_wrapper(Iterator it = Iterator())
: m_it(it) { }
// Typedefs, Operators etc.
// i.e.
using value_type = typename Iterator::ValueType;
using difference_type = std::ptrdiff_t;
difference_type operator- (iterator_wrapper const &rhs) const
{
return m_it - rhs.m_it;
}
};
}
注意:也可以使iterator_wrapper
继承自Iterator
,或者使其更通用,并有另一个帮助程序来启用其他迭代器的包装。
现在begin()
和end()
:
namespace ThirdPartyLib
{
template<class T>
ThirdPartyStdAdaptor::iterator_wrapper<typename
SomeContainer<T>::iterator> begin(SomeContainer<T> &c)
{
return ThirdPartyStdAdaptor::iterator_wrapper<typename
SomeContainer<T>::iterator>(c.begin());
}
template<class T>
ThirdPartyStdAdaptor::iterator_wrapper < typename
SomeContainer<T>::iterator > end(SomeContainer<T> &c)
{
return ThirdPartyStdAdaptor::iterator_wrapper < typename
SomeContainer<T>::iterator > (c.end());
}
}
(也可以将它们放在与SomeContainer
不同的命名空间中但是ADL松散。如果该容器的命名空间中存在begin
和end
函数,我会倾向于将适配器重命名为wbegin
和wend
。)
现在可以使用这些函数调用标准算法:
ThirdPartyLib::SomeContainer<SomeType> test;
std::ptrdiff_t d = std::distance(begin(test), end(test));
如果库名称空间中包含begin()
和end()
,则容器甚至可以在更通用的上下文中使用。
template<class T>
std::ptrdiff_t generic_range_size(T const &x)
{
using std::begin;
using std::end;
return std::distance(begin(x), end(x));
}
此类代码可以与std::vector
以及ThirdPartyLib::SomeContainer
一起使用,只要ADL找到begin()
和end()
返回包装器迭代器。
答案 3 :(得分:0)
您可以使用Container
作为模板参数来iterator_traits
。对STL的其余部分来说最重要的是traits类中的typedef,例如value_type
。那些应该正确设置:
template <class Container> struct iterator_traits
{
public:
typedef typename Container::value_type value_type;
// etc.
};
然后,您可以使用之前使用value_type
的{{1}}。
至于使用traits类,您当然会使用外部容器的类型对其进行参数化:
T
当然,这假设iterator_traits<TheContainer> traits;
符合常见的STL容器合同,并且TheContainer
已正确定义。