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

using nested-types of a template-class as template parameter

本文关键字:参数 嵌套类型      更新时间:2023-10-16

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

我刚刚在这里读到,最好将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

由于您已经澄清了很多问题,我的第一个答案不再适用,我将删除编辑它,为您提供更适合的内容:

更新答案:您希望将模板约束为仅接受在MyClass模板内进行类型定义的类型。这些约束通常通过应用SFINAE来实现,尤其是通过std::enable_if(或者boost::enable_if,如果您的库缺少C++11的支持)。遗憾的是,没有像is_typedeffed_inside这样的特征可以用于您的案例。更糟糕的是:没有办法只使用普通的typedef来编写这样的特性,因为在给定的类中进行typedef没有什么特别的——编译器没有办法确定(也不感兴趣)给定的已知类型是否在某个地方有别名。

但是,如果你的typedef只是你在问题中显示的类型,我有个好消息要告诉你:你需要两个operator<<

  1. 一个用于boost::dynamic_bitset<>,因为这是任何MyClass实例化的BoolTable
  2. 另一个是模板化的,用于std::vector<T>,因为这是每个相应MyClass<T>的MsgTable

缺点是,使用这个模板化的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); 
}

您原来的类很好。的确,如果您希望有一个operator <<来写入流,那么它应该是一个非成员非好友函数,就像您所拥有的一样,但该函数没有理由不能调用公共成员函数来完成这项工作。

我终于找到了这个类似的问题

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

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

我相信你混淆了一些东西。typename只是为了能够将它与其他模板参数分开。尝试将其重命名为

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

然后像使用对象一样使用它。

请参见此处。