c++templatesnestedtemplate-functionstream-operators

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


I want to implement a template function using nested-types of a template-class.

I have just read here that it is better to implement operator << as non-member and non-friend function. Therefore I decided to move functions toStream() and tableToStream() outside 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);
}

It's easy to convert MyClass::toStream() into an operator << non-member and non-friend function:

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

But I want to use solely operator << instead of calling 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;
}

For the function MyClass::tableToStream() I could use the following implementation, but this may mess the stream output because the function is too generic (any type can be 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;
}

Therefore, I want to restrict to the nested types of MyClass. Below is one of my attempts to convert MyClass::tableToStream() into a standard operator << non-member and non-friend function:

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

But the error is about typename MyClass<T>::TABLE.


Solution

  • Since you have clarified your question a lot, my first answer does not apply any more and I'll remove-edit it to give you something that might fit better:

    Updated answer: You want to constrain the template to accept only types that are typedeffed inside your MyClass template. Such constraints are usually achieved by application of SFINAE, especially by std::enable_if (or boost::enable_if, if your library lacks that part of C++11 support). Sadly, there is no traits like a is_typedeffed_inside that could be used for your case. It's even worse: there is no way to write such a trait just using the plain typedefs, since there is nothing special about being typedeffed inside a given class - the compiler has no way to determine (and is not interested in) if a given known type has some alias name for it somewhere.

    But if your typedefs are just the ones you show in your question, I have good news for you: you need exactly two operator<< for that:

    1. One for boost::dynamic_bitset<>, since that is the BoolTable for any MyClass instantiation.
    2. Another one, templated, for std::vector<T>, since that is the MsgTable for each corresponding MyClass<T>.

    The downside is, that with this templated operator<<, you'd be able to output any std::vector<FooBar>, even if FooBar is completely unrelated to any use of MyClass. But that holds for any other possible implementation of the proper operator<<'s - if there's no explicit restriction on the MSG parameter, there's no restriction on a FooBar making a std::vector<FooBar> a viable MyClass<MSG>::MsgTable.

    My conclusion for your question: you wanted to have the operator<< for its convenient looks, since it is normally used for thet purpose. In your case, you can provide it for MyClass<MSG> objects, but there is no way to do so for the inner typedefs alone.

    I'd implement it that way:

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