C ++中的模板化地图/多图功能

时间:2010-07-08 13:01:50

标签: c++ templates

假设我有:

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

5 个答案:

答案 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行方法。