【问题标题】:template classes as template parameters模板类作为模板参数
【发布时间】:2014-12-06 20:26:09
【问题描述】:

如何在容器和类型上定义函数模板化?

例如,重载插入运算符以流式传输向量、列表或前向迭代器容器的所有元素:

using namespace std;

#include <iostream>
#include <vector>
#include <list>


//...
//...the second argument is a container template-ed on type T
//...
template <typename T,template <typename U> class C>
ostream&
operator<<
  (ostream& p_os,const C<T>& p_c)
{
  for(typename C<typename T>::const_iterator cit=p_c.begin();cit!=p_c.end();++cit)
  {
    p_os.operator<<(*cit);
  }
  return p_os;
}

int
main
  ()
{
  vector<int> v;
  cout << v << endl;
  list<int> l;
  cout << l << endl;
  return 0;
}

这不能在 g++ 4.9 上编译。怎么了?是怎么做到的?

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    为什么不直接将容器类型作为模板参数传递,并从中找出元素类型呢?在您的示例代码中,您甚至不需要元素类型:

    template <typename C>
    ostream&
    operator<<
      (ostream& p_os,const C& p_c)
    {
      typedef typename C::value_type element_type; // if needed
      for(typename C::const_iterator cit=p_c.begin();cit!=p_c.end();++cit)
      {
        p_os.operator<<(*cit);
      }
      return p_os;
    }
    

    (尽管在没有 enable_if 技巧的情况下将其用于像这样的全局函数可能是不明智的,因为否则它将匹配任何参数。)

    编辑:例如,您可以尝试将其限制为具有嵌套 value_type 的类(所有容器都有):

    template <typename C, typename T = typename C::value_type>
    ostream&
    operator<<
      (ostream& p_os,const C& p_c)
    

    【讨论】:

    • 这种方法符合我的要求。我已对此进行了扩展以支持流式传输所有容器类型
    • "(尽管在没有一些 enable_if 技巧的情况下将其用于像这样的全局函数可能是不明智的,因为否则它将匹配任何参数。)" 我尝试使用逗号分隔容器,但编译器试图使用该函数来流式传输字符串。如何使用 enable_if 避免这种情况?
    • 查看编辑(实际上并未使用enable_if)。如需更复杂、更强大的解决方案,请参阅stackoverflow.com/questions/9242209/…
    【解决方案2】:

    std::vector 是一个具有两个模板类型参数的类模板:

    template <class T, class Alloc = allocator<T> >
    class vector;
    

    要使您的函数与std::vector(和其他双参数类模板)一起使用,您可以使用以下定义:

    template <typename T, typename A, template <typename, typename> class C>
    //                    ~~~~~~~~~^                      ~~~~~~~^
    ostream& operator<<(ostream& p_os, const C<T,A>& p_c) 
    //                                          ^^
    {
      for(typename C<T,A>::const_iterator cit=p_c.begin();cit!=p_c.end();++cit)
      {
         p_os.operator<<(*cit);
      }
      return p_os;
    }
    

    或者:

    template <typename T, template <typename...> class C>
    ostream& operator<<(ostream& p_os, const C<T>& p_c);
    

    【讨论】:

    • vector至少两个模板参数——一个实现可以添加更多,只要它们有默认值。所以第一个版本是不可移植的。
    • @AlanStokes 我不相信你,标准在哪里这么说?
    • 我很抱歉,这条“常识”证明不是真的。被提议但被拒绝:stackoverflow.com/questions/1469743/…
    • 分配器有一个默认值,因此,如果默认分配器是可接受的,则这种方法是不必要的。
    • @silvermangb 需要将向量与模板模板参数进行匹配
    【解决方案3】:

    艾伦·斯托克斯的方法很有效。下面的代码可以流式传输任何容器。我只需要为地图添加一个插入运算符

    using namespace std;
    
    #include <iostream>
    
    #include <vector>
    #include <list>
    #include <forward_list>
    #include <set>
    #include <deque>
    #include <array>
    #include <map>
    #include <unordered_map>
    
    //...
    //...needed for map types which are (key,value) pairs.
    //...
    template <typename K,typename V>
    ostream&
    operator<<
      (ostream& p_os,const pair<const K,V>& p_v)
    {
      std::operator<<(p_os,'(');
      p_os << p_v.first;
      std::operator<<(p_os,',');
      p_os << p_v.second;
      std::operator<<(p_os,')');
      return p_os;
    }
    
    template <typename C, typename T = typename C::iterator>
    ostream&
    operator<<
      (ostream& p_os,const C& p_c)
    {
      for(typename C::const_iterator cit=p_c.begin();cit!=p_c.end();++cit)
      {
        typename C::value_type v = *cit;
        p_os << v;
        std::operator<<(p_os,",");
      }
      return p_os;
    }
    
    int
    main
      ()
    {
      vector<int> v;
      for(int i=0;i<4;++i)
      {
        v.push_back(i);
      }
      cout << v << endl;
      list<int> l;
      for(int i=0;i<4;++i)
      {
        l.push_back(i);
      }
      cout << l << endl;
      forward_list<int> fl = {0,1,2,3};
      cout << fl << endl;
      set<int> s;
      for(int i=0;i<4;++i)
      {
        s.insert(i);
      }
      cout << s << endl;
      deque<int> d;
      for(int i=0;i<4;++i)
      {
        d.push_back(i);
      }
      cout << d << endl;
      array<int,4> a = {0,1,2,3};
      cout << a << endl;
      unordered_map<int,int> um;
      for(int i=0;i<4;++i)
      {
        um[i] = i;
      }
      cout << um << endl;
      map<int,int> m;
      for(int i=0;i<4;++i)
      {
        m[i] = i;
      }
      cout << m << endl;
      return 0;
    }
    

    【讨论】:

    • 仍然不完全正确。导致 operator 的歧义
    • 嗯,string 是一个容器,所以它应该匹配它。
    • 不过,我想要一个字符串的解决方法。在不阻止字符串或其他对象被序列化的情况下序列化任何容器运算符
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多