[英]specializing iterator_traits
I'd like to specialize std::iterator_traits<>
for iterators of a container class template that does not have the usual nested typedefs (like value_type
, difference_type
, etc.) and whose source I shouldn't modify. 我想专门
std::iterator_traits<>
对于不具有通常的嵌套的typedef(等的容器类模板的迭代器value_type
, difference_type
等)和其源我不应该修改。 Basically I'd like to do something like this: 基本上我想做这样的事情:
template <typename T> struct iterator_traits<typename Container<T>::iterator>
{
typedef T value_type;
// etc.
};
except that this doesn't work, as the compiler is unable to deduce T
from Container<T>::iterator
. 除了这不起作用,因为编译器无法从
Container<T>::iterator
推导出T
Is there any working way to achieve the same? 是否有任何工作方式来实现相同的目标?
For example: 例如:
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() { ... }
...
};
Now suppose I call std::count()
using an instance of this class. 现在假设我使用此类的实例调用
std::count()
。 As far as I know, in most STL implementations, count()
returns iterator_traits<Iterator>::difference_type
. 据我所知,在大多数STL实现中,
count()
返回iterator_traits<Iterator>::difference_type
。 The primary template of iterator_traits<I>
simply does typedef typename I::difference_type difference_type
. iterator_traits<I>
的主要模板只是typedef typename I::difference_type difference_type
。 Same with the other nested types. 与其他嵌套类型相同。
Now in our example this obviously won't work, as there's no Container::iterator::difference_type
. 现在在我们的例子中,这显然不起作用,因为没有
Container::iterator::difference_type
。 I thought I could work around this without modifying the iterator class, by specializing iterator_traits
for iterators of any Container<T>
. 我以为我可以解决这个不修改迭代器类,由专门
iterator_traits
任何的迭代器Container<T>
In the end, I just want to be able to use std algorithms like count, find, sort, etc., preferably without modifying any existing code. 最后,我只想使用std算法,如count,find,sort等,最好不要修改任何现有的代码。 I thought that the whole point of
iterator_traits
is exactly that: being able to specify types (like value_type
, diff_type
etc.) for iterator types that do not support them built-in. 我认为
iterator_traits
的重点在于:能够为不支持内置类型的迭代器类型指定类型(如value_type
, diff_type
等)。 Unfortunately I can't figure out how to specialize the traits class for all instances of Container<T>
. 不幸的是,我无法弄清楚如何为
Container<T>
所有实例专门化traits类。
Yes. 是。 The compiler cannot deduce
T
from Container<T>::iterator
because it is non-deducible context, which in other words means, given Container<T>::iterator
, the value of T
cannot uniquely and reliably be deduced (see this for detail explanation ). 编译器不能从
Container<T>::iterator
推导出T
,因为它是不可导入的上下文,换句话说,在给定Container<T>::iterator
, T
的值不能唯一且可靠地推导出来(参见此内容)详细解释 )。
The only solution to this problem is that you've to fully specialize iterator_traits
for each possible value of iterator
which you intend to use in your program. 这个问题的唯一解决方案是你要为你想在程序中使用的
iterator
iterator_traits
的每个可能值完全专门化iterator_traits
。 There is no generic solution, as you're not allowed to edit the Container<T>
class template. 没有通用的解决方案,因为您不允许编辑
Container<T>
类模板。
Nawaz's answer is likely the right solution for most cases. 对于大多数情况, Nawaz的答案可能是正确的解决方案。 However, if you're trying to do this for many instantiated
SomeContainerFromAThirdPartyLib<T>
classes and only a few functions (or an unknown number of instantiations but a fixed number of functions, as might happen if you're writing your own library), there's another way. 但是,如果您尝试为许多实例化的
SomeContainerFromAThirdPartyLib<T>
类以及仅少数函数(或未知数量的实例化但是固定数量的函数,如果您正在编写自己的库时可能会发生),还有另一种方式。
Assume we're given the following (unchangeable) code: 假设我们得到以下(不可更改的)代码:
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(); }
};
}
We define an adapter class template containing the necessary typedef
s for iterator_traits
and specialize it to avoid problems with pointers: 我们定义了一个适配器类模板,其中包含
iterator_traits
所需的typedef
,并将其专门化以避免指针出现问题:
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*>
{
};
}
Then, for each function we want to be able to call with a SomeContainerFromAThirdPartyLib::iterator
, we define an overload and use SFINAE: 然后,对于我们希望能够使用
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);
}
We can then use it as follows: 然后我们可以按如下方式使用它:
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;
}
You can find a runnable example with the required include
s and using
s at http://ideone.com/gJyGxU . 您可以找到所需的可运行的例子
include
S和using
s的http://ideone.com/gJyGxU 。 The output: 输出:
a=Hello, world count(a, a + sizeof(a), 'l')=3 count(container.begin(), container.end(), 0)=[in adapter version of count]0
Unfortunately, there are caveats: 不幸的是,有一些警告:
find
, sort
, et cetera). find
, sort
等)。 This obviously won't work for functions in algorithm
that haven't been defined yet. algorithm
中的函数。 In regards to that last one, the question is in which namespace to put the overload (and how to call the std
version). 关于最后一个,问题是在哪个命名空间中放置重载(以及如何调用
std
版本)。 Ideally, it would be in ThirdPartyLib
so that it could be found by argument-dependant lookup, but I've assumed we can't change that. 理想情况下,它将在
ThirdPartyLib
以便可以通过参数依赖查找找到它,但我认为我们无法改变它。 The next best option is in MyLib
, but then the call has to be qualified or preceded by a using
. 下一个最佳选择是在
MyLib
,但是然后必须限定调用或在using
之前。 In either case the end-user should either use using std::count;
在任何一种情况下,最终用户都应该
using std::count;
or be careful about which calls to qualify with std::
, since if std::count
is mistakenly used with SomeContainerFromAThirdPartyLib::iterator
, it will obviously fail (the whole reason for this exercise). 或者注意哪些调用符合
std::
,因为如果std::count
错误地与SomeContainerFromAThirdPartyLib::iterator
一起SomeContainerFromAThirdPartyLib::iterator
,它显然会失败(这个练习的全部原因)。
An alternative that I do not suggest but present here for completeness would be to put it directly in the std
namespace. 我不建议但在此处提供完整性的替代方法是将其直接放在
std
命名空间中。 This would cause undefined behavior; 这会导致未定义的行为; while it might work for you, there's nothing in the standard that guarantees it.
虽然它可能对你有用,但标准中没有任何东西可以保证它。 If we were specializing
count
instead of overloading it, this would be legal. 如果我们专门
count
而不是重载,这将是合法的。
In the specialization in question, T
is in a nondeducible context but there is neither a third party library container code change nor any specialization in the std
namespace required. 在所讨论的专业化中,
T
处于不可约的上下文中,但是既没有第三方库容器代码更改,也没有所需的std
命名空间中的任何特化。
If the third party library does not provide any free begin
and end
functions in the respective namespace one can write own functions (into that namespace if desired to enable ADL) and wrap the iterator into an own wrapper class which in turn provides the necessary typedefs and operators. 如果第三方库没有在相应的命名空间中提供任何自由的
begin
和end
函数,则可以编写自己的函数(如果需要启用ADL,则进入该命名空间)并将迭代器包装到自己的包装类中,该包装类又提供必要的typedef和运营商。
First one needs the Iterator wrapper. 第一个需要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;
}
};
}
Note: It would also be possible to make iterator_wrapper
inherit from Iterator
, or to make it more generic and have another helper to enable the wrapping of other iterators as well. 注意:也可以使
iterator_wrapper
继承自Iterator
,或者使其更通用,并有另一个帮助程序来启用其他迭代器的包装。
Now begin()
and end()
: 现在
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());
}
}
(It is also possible to have them in a different namespace than SomeContainer
but loose ADL. IF there are begin
and end
functions present in the namespace for that container I'd tend to rename the adaptors to be something like wbegin
and wend
.) (也可以将它们放在与
SomeContainer
不同的命名空间中,但松散的ADL。如果在该容器的命名空间中存在begin
和end
函数,我倾向于将适配器重命名为wbegin
和wend
。)
The standard algorithms can be called using those functions now: 现在可以使用这些函数调用标准算法:
ThirdPartyLib::SomeContainer<SomeType> test;
std::ptrdiff_t d = std::distance(begin(test), end(test));
If begin()
and end()
are included into the library namespace, the container can even be used in more generic contexts. 如果将
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));
}
Such code can be used with std::vector
as well as ThirdPartyLib::SomeContainer
, as long as ADL finds begin()
and end()
returning the wrapper iterator. 这样的代码可以与
std::vector
以及ThirdPartyLib::SomeContainer
,只要ADL找到begin()
和end()
返回包装器迭代器。
You can very well use the Container
as template parameter to your iterator_traits
. 您可以很好地将
Container
作为模板参数用于iterator_traits
。 What matters to the rest of STL are the typedefs inside your traits class, such as value_type
. 对STL的其余部分重要的是traits类中的typedef,例如
value_type
。 Those should be set correctly: 那些应该正确设置:
template <class Container> struct iterator_traits
{
public:
typedef typename Container::value_type value_type;
// etc.
};
You would then use value_type
where you would previously use T
. 然后,您将使用之前使用
T
value_type
。
As for using the traits class, you of course parametrize it with the type of your external container: 至于使用traits类,您当然可以使用外部容器的类型对其进行参数化:
iterator_traits<TheContainer> traits;
Naturally, this assumes TheContainer
is conforms to the common STL containers' contract and has value_type
defined correctly. 当然,这假设
TheContainer
符合常见的STL容器契约并且正确定义了value_type
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.