假设我有:
std::map<K, V1> m1;
std::multimap<K, V2> m2;
我想按容器类型和键/值类型进行模板化。但以下情况不起作用:/
template <typename T>
void do_something(T var)
{
// do something
}
template <typename TContainer, typename TKey, typename TVal>
void func(const TContainer<TKey, TVal>& container)
{
for (typename TContainer<TKey, TVal>::iterator it = container.begin(); it != container.end(); ++it)
{
do_something(it->second);
}
}
然后用:
来调用它func(m1);
func(m2);
答案 0 :(得分:7)
你不能只有一个模板参数吗?
template <typename Container>
void func(const Container & container)
{
for (typename Container::iterator it = container.begin(); it != container.end(); ++it)
{
do_something(it->second);
}
}
或者更好的是,将迭代器传递给函数而不是容器:
template <typename ForwardIterator>
void func(ForwardIterator begin, ForwardIterator end)
{
for (; begin != end; ++begin)
{
do_something(begin->second);
}
}
如果您真的需要模板模板参数,请使用以下语法:
template <template <typename, typename> Container, typename TKey, typename TValue>
void func(const Container<TKey, TValue> & container);
然而,这对STL容器不起作用,因为它们通常具有比看起来更多的参数;实际上,它们通常具有默认值的参数,例如allocator,所以最好的办法是使用上面描述的通用算法的惯用方法,即处理迭代器而不是容器。
答案 1 :(得分:7)
这不起作用的原因是std::map
需要 四个 模板参数:
template<class Key,
class Value,
class Predicate = std::less<Key>,
class Allocator = std::allocator<pair<const Key, Value> > >
class map;
虽然您可以省略最后两个用于实例化的参数,但您必须列出它们才能使模板匹配起作用:
template < typename TKey,
typename TVal,
class TPr,
class TAl
template<typename,typename,class,class> TContainer >
void func(const TContainer<TKey, TVal, TPr, TAl>& container)
{
for (typename TContainer<TKey, TVal, TPr, TAl>::iterator it = container.begin(); it != container.end(); ++it)
{
do_something(it->second);
}
}
然而,那说,我想知道你为什么要这么烦。惯用的方法是传递迭代器:
template <typename FwdIt>
void func(FwdIt begin, FwdIt end)
{
while(begin != end) {
do_something(begin->second);
++begin;
}
}
这也允许您传递任何兼容的内容:
void f(const std::vector< std::pair<int, std::string> >& v)
{
func( v.begin(), v.end() );
}
答案 2 :(得分:1)
多次建议传递迭代器是标准方法,但您也可以利用Boost.Range:
#include <boost/range.hpp>
template<typename ForwardReadableRange>
void func(const ForwardReadableRange& range) {
typedef typename boost::range_iterator<const ForwardReadableRange>::type InputIterator;
for (InputIterator it = boost::begin(range); it != boost::end(range); ++it) {
do_something(it->second);
}
}
template<typename ForwardReadableWriteableRange>
void func(ForwardReadableWriteableRange& range) {
typedef typename boost::range_iterator<ForwardReadableWriteableRange>::type ForwardIterator;
for (ForwardIterator it = boost::begin(range); it != boost::end(range); ++it) {
do_something(it->second);
}
}
这允许调用者传递模仿ForwardReadable(Writeable)Range
的任何内容,例如容器或迭代器对。
当然,这应该由DoSomethingWithSecond
仿函数和for_each
替换:
template<typename T, typename UnaryOp, typename Result>
struct DoSomethingWithSecond: std::unary_function<T, Result> {
UnaryOp op;
explicit DoSomethingWithSecond(UnaryOp op): op(op) { }
Result operator()(T value) {
return op(value.second);
}
};
template<typename T>
void func(T range) {
boost::for_each(range, DoSomethingWithSecond(do_something));
}
答案 3 :(得分:0)
试试这个,你不需要指定容器是在键和值类型上模板化的(请注意,对于任何类型的代码都是相同的),如果传递的类型没有,编译器将生成错误满足合同。目前合同是“有迭代器”,“已开始”,“已结束”和“迭代器有第二”。
template <typename T>
void do_something(T var) // Also consider "const T&"
{
// do something
}
template <typename TContainer>
void func(const TContainer& container)
{
for (typename TContainer::iterator it = container.begin();
it != container.end(); ++it)
{
do_something(it->second);
}
}
答案 4 :(得分:0)
我肯定会建议采用以下方法。由于很多原因,我不喜欢在容器上进行模板化,所以让我们在迭代器上模板化。一个繁琐但通用的方法如下:
#include <algorithm>
#include <functional>
// This should be part of the standard, but it isn't.
template <typename Func1, typename Func2>
struct composer :
std::unary_function
<
typename Func2::argument_type,
typename Func1::result_type
>
{
composer(Func1 f1_ = Func1(), Func2 f2_ = Func2())
: f1(f1_), f2(f2_)
{}
typename Func1::result_type
operator()(typename Func2::argument_type x)
{ return f1(f2(x)); }
private:
Func1 f1; Func2 f2;
};
template <typename F1, typename F2>
composer<F1, F2> compose(F1 f1, F2 f2)
{ return composer<F1, F2>(f1, f2); }
template <class C, typename T, T C::*ptr>
struct mem_ptr : std::unary_function<C&, T&>
{ T& operator()(C& x) { return x::*ptr; } };
template <typename Iter>
void func(Iter begin, Iter end)
{
typedef typename Iter::value_type pair_t;
typedef mem_ptr
<
pair_t,
pair_t::second_type,
pair_t::&second
> second_of;
std::for_each(begin, end, compose(ptr_fun(do_something), ptr_mem));
}
效用函数compose
和类mem_ptr
是应该在标准中实现的东西(尽管它们可能在TR1中)。请注意,您可以对do_something
的类型进行模板设置,甚至可以将do_something
作为参数传递。
请注意,ptr_mem
可以改进为您可以调用的内容,如
ptr_mem(pair_t::&second)
但这会涉及更多代码。 Boost中可能有一些有用的东西,但在这里我们可以满足于20行方法。