假设我想编写一个算法来打印容器中每个元素的值。容器可以是Sequence
或Associative
容器(例如std::vector
或std::map
)。在序列的情况下,算法将打印value_type
。在关联类型的情况下,算法将打印data_type
。如何编写我的算法(仅一次!)以便它可以与任何一个一起使用?假设算法很复杂,并且我不想为序列/关联版本重复它。
例如:
template <class Iterator>
void printSequence(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << *it;
}
template <class Iterator>
void printAssociative(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << it->second;
}
template <class Iterator>
void printEither(Iterator begin, Iterator end)
{
// ????
}
答案 0 :(得分:4)
两个函数模板之间的区别不在于关联容器和序列之间的差异,而是存储类型部分的差异。
为了澄清,std::set
是一个关联容器,但可以与printSequence
函数一起使用; map
的问题不在于它是关联的,而是value_type
是pair
,您只对second
部分感兴趣。
最简单的方法是抽象解除引用操作。
E.g。像这样使用:
#include <map>
#include <vector>
template< class X, class Y >
void test( const std::map<X, Y>& mp )
{
printEither( mp.begin(), mp.end(), MakeMapDerefence( mp ) );
}
template< class Y >
void test( const std::vector<Y>& vec )
{
printEither( vec.begin(), vec.end(), MakeSimpleDereference( vec ) );
}
定义如下(有一个锅炉板可能是一个提升单线):
template< class ReferenceType, class IteratorType >
struct SimpleDereference
{
ReferenceType operator() ( IteratorType i ) const
{
return *i;
}
};
template< class ReferenceType, class IteratorType >
struct MapDereference
{
ReferenceType operator() ( IteratorType i ) const
{
return i->second;
}
};
// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::const_reference
, typename Container::const_iterator >
MakeSimpleDereference( const Container& )
{
return SimpleDereference< typename Container::const_reference
, typename Container::const_iterator >();
}
// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::reference
, typename Container::iterator >
MakeSimpleDereference( Container& )
{
return SimpleDereference< typename Container::reference
, typename Container::iterator >();
}
// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< const typename Container::mapped_type&
, typename Container::const_iterator >
MakeMapDerefence( const Container& )
{
return MapDereference< const typename Container::mapped_type&
, typename Container::const_iterator >();
}
// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< typename Container::mapped_type&
, typename Container::iterator >
MakeMapDereference( Container& )
{
return MapDereference< typename Container::mapped_type&
, typename Container::iterator >();
}
#include <iostream>
#include <ostream>
template <class Iterator, class Dereference> void printEither(Iterator begin, Iterator end, Dereference deref)
{
for (; begin != end; ++begin)
{
std::cout << deref(begin);
}
}
答案 1 :(得分:1)
我根据Charles的回答掀起了一个迭代器适配器。我在这里张贴以防万一有人发现它有用:
#include <iostream>
#include <map>
#include <vector>
#include <boost/iterator/iterator_adaptor.hpp>
//------------------------------------------------------------------------------
template <class Iterator>
void print(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << *it << "\n";
}
//------------------------------------------------------------------------------
template <class BaseIterator>
class MapDataIterator :
public boost::iterator_adaptor<
MapDataIterator<BaseIterator>,
BaseIterator,
typename BaseIterator::value_type::second_type >
{
public:
typedef typename BaseIterator::value_type::second_type& reference;
MapDataIterator() {}
explicit MapDataIterator(BaseIterator base)
: MapDataIterator::iterator_adaptor_(base) {}
private:
friend class boost::iterator_core_access;
reference dereference() const
{return this->base_reference()->second;}
};
//------------------------------------------------------------------------------
int main()
{
std::vector<int> vec;
vec.push_back(31);
vec.push_back(41);
std::map<int,int> map;
map[31] = 41;
map[59] = 26;
typedef MapDataIterator< std::map<int,int>::iterator > DataIter;
print( vec.begin(), vec.end() );
print( DataIter(map.begin()), DataIter(map.end()) );
}
此解决方案的另一个优点是算法无需知道如何取消引用迭代器。对于任何需要“数据序列”的现有算法,它也是可重用的。
我很惊讶这个小动物在Boost中不存在。