使用模板类的嵌套类型作为模板参数

时间:2013-02-20 14:45:39

标签: c++ templates nested template-function stream-operators

我想使用嵌套类型的模板类来实现模板函数。

我刚刚阅读here,最好将operator <<实现为非成员和非朋友功能。因此,我决定将函数toStream()tableToStream()移到MyClass之外:

template <typename T>
class MyClass
{
public:
  typedef boost::dynamic_bitset<> BoolTable; 
  typedef std::vector<T>          MsgTable;
private:
  BoolTable  b_;
  MsgTable   m_;
public:
  const BoolTable& getB() const { return b_; }
  const MsgTable & getM() const { return m_; }

  std::ostream& toStream (std::ostream& os) const
  {
    os <<"Bool: ";  tableToStream (os, getB());  os <<'\n';
    os <<"Msg:";    tableToStream (os, getM());  os <<'\n';
    return os;
  }

  template <typename TABLE>
  std::ostream& tableToStream (std::ostream& os, const TABLE& table) const
  {
    for (int i=0; i < table.size(); ++i)
      os << table[i] <<',';
    return os;
  }
};

template <typename T>
std::ostream& operator << (std::ostream& os, const MyClass<T> mc)
{
  return mc.toStream(os);
}

MyClass::toStream()转换为operator <<非会员和非朋友功能很容易:

template <typename T>
std::ostream& operator << (std::ostream& os, const MyClass<T>& mc)
{
  os <<"Bool: ";  mc.tableToStream (os, mc.getB());  os <<'\n';
  os <<"Msg:";    mc.tableToStream (os, mc.getM());  os <<'\n';
   return os;
}

但我想仅使用operator <<而不是调用MyClass::tableToStream()

template <typename T>
std::ostream& operator << (std::ostream& os, const MyClass<T>& mc)
{
  os <<"Bool: "<< mc.getB() <<'\n';
  os <<"Msg:"  << mc.getM() <<'\n';
   return os;
}

对于函数MyClass::tableToStream(),我可以使用以下实现,但这可能会使流输出混乱,因为该函数过于通用(任何类型都可以是TABLE)。

template <typename TABLE>
std::ostream& operator << (std::ostream& os, const TABLE& table) 
{
  for (int i=0; i < table.size(); ++i)
    os << table[i] <<',';
  return os;
}

因此,我想限制为MyClass嵌套类型。以下是我尝试将MyClass::tableToStream()转换为标准operator <<非会员和非朋友功能的尝试之一:

template <typename T, typename MyClass<T>::TABLE>
std::ostream& operator << (std::ostream& os, const TABLE& table) 
{
  for (int i=0; i < table.size(); ++i)
    os << table[i] <<',';
  return os;
}

但错误大约是typename MyClass<T>::TABLE

4 个答案:

答案 0 :(得分:1)

由于你已经澄清了很多问题,我的第一个答案不再适用了,我将删除 - 编辑它以给你一些可能更合适的东西:

更新回答: 您希望将模板约束为仅接受MyClass模板中typedeffed的类型。这些约束通常通过应用SFINAE来实现,尤其是std::enable_if(或boost::enable_if,如果您的库缺少C ++ 11支持的那部分)。可悲的是,没有像is_typedeffed_inside这样可以用于你的案例的特征。更糟糕的是:没有办法只使用普通的typedef编写这样的特性,因为在给定的类中没有任何特殊的类型化 - 编译器无法确定(并且不感兴趣) type在某处有一些别名。

但是如果你的typedef只是你在问题中显示的那些,我就会有好消息:你需要两个operator<<

  1. 一个用于boost::dynamic_bitset<>,因为那是任何 MyClass实例化的BoolTable。
  2. 另一个,模板化,std::vector<T>,因为这是每个对应MyClass<T>的MsgTable。
  3. 缺点是,使用此模板operator<<,您可以输出任何std::vector<FooBar>,即使FooBar与任何使用MyClass完全无关。但这适用于正确的operator<<的任何其他可能的实现 - 如果对MSG参数没有明确的限制,则对FooBar没有限制使std::vector<FooBar>成为可行的MyClass<MSG>::MsgTable

    我对您的问题的结论:您希望operator<<具有方便的外观,因为它通常用于此目的。在您的情况下,您可以为MyClass<MSG>个对象提供它,但单独的内部typedef无法这样做。

    我会这样实现:

    template <class MSG>
    class MyClass {
      /* ... */
    public:
      // instead of declaring op<< a friend, redirect to a method that has 
      // natural access to private members
      std::ostream& printToStream(std::ostream& os) const
      {
        os << "Bool: "; 
        tableToStream (getB(), os); 
        os <<"\nMsg:";   
        tableToStream (getM(), os); 
        return os <<'\n';
      }
    private:
      // make this one private so nobody can misuse it to print unrelated stuff
      template <class Table>
      static void tableToStream(Table const& table, std::ostream& os)
      {
        std::copy(begin(table), end(table), ostream_iterator(os, ", "));    
      }
    };
    
    template <typename MSG>
    std::ostream& operator << (std::ostream& os, const MyClass<MSG>& mc)
    {
      return mc.printToStream(os); 
    }
    

答案 1 :(得分:1)

你原来的课很好。确实如果你想要一个operator <<来写一个流,它应该是一个非成员的非朋友函数,就像你一样,但没有理由这个函数不能调用公共成员函数来完成工作。

答案 2 :(得分:0)

我终于找到了this similar question

在我的情况下,解决方案是:

template <typename T>
std::ostream& operator << (std::ostream& os, 
                           typename MyClass<T>::TABLE const& table) 
{
  for (int i=0; i < table.size(); ++i)
    os << table[i] <<',';
  return os;
}

更新:正如@ArneMertz所指出的,上述功能不起作用。
以下是我测试的完整代码:

#include <ostream>
#include <boost/dynamic_bitset.hpp>

template <typename T>
class MyClass  
{
  public:
    typedef boost::dynamic_bitset<> BoolTable; 
    typedef std::vector<T>          MsgTable;

    BoolTable  b_;
    MsgTable   m_;

    const BoolTable& getB() const { return b_; }
    const MsgTable & getM() const { return m_; }
};

template <typename T>
std::ostream& operator << (std::ostream& os, 
                           typename MyClass<T>::TABLE const& table) 
{
  for (int i=0; i < table.size(); ++i)
    os << table[i] <<',';
  return os;
}

template <typename T>
std::ostream& operator << (std::ostream& os, const MyClass<T>& mc)  
{
    os <<"Bool: "<< mc.getB() <<'\n'; // <-- this line is OK because it
    os <<"Msg:  "<< mc.getM() <<'\n';            //uses boost operator<<
    return os;
}

main函数:

#include <iostream>

int main()
{
  MyClass<int> var;
  var.b_.push_back(true);
  var.b_.push_back(false);
  var.b_.push_back(true);
  var.m_.push_back(23);
  var.m_.push_back(24);
  var.m_.push_back(25);

  std::cout << var;
}

答案 3 :(得分:-1)

我相信你很困惑。 typename只是为了能够从其他模板参数中分离出来。尝试将其重命名为

template <typename OS, typename MSG, typename MSGTable>
OS& operator << (OS& os, const MSGTable& table) const{}

然后像对象一样使用它。

请参阅here