Использование nested-типов шаблона-класса в качестве параметра шаблона

Я хочу реализовать шаблонную функцию, используя вложенные типы шаблонного класса.

Я только что прочитал здесь, что лучше реализовать 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 ответа

Решение

Так как вы много раз прояснили свой вопрос, мой первый ответ больше не применяется, и я удалю-отредактирую его, чтобы дать вам кое-что, что могло бы подойти лучше:

Обновленный ответ: вы хотите ограничить использование шаблона только теми типами, которые определены в вашем шаблоне MyClass. Такие ограничения обычно достигаются путем применения SFINAE, особенно путем std::enable_if (или же boost::enable_if, если вашей библиотеке не хватает этой части поддержки C++11). К сожалению, нет таких черт, как is_typedeffed_inside это может быть использовано для вашего случая. Еще хуже: нет способа написать такую ​​черту, просто используя простые определения типов, так как нет ничего особенного в том, чтобы определить тип внутри определенного класса - компилятор не может определить (и не заинтересован), является ли данный известный Тип имеет псевдоним для него где-то.

Но если ваши typedefs - это те, которые вы показываете в своем вопросе, у меня для вас хорошие новости: вам нужно ровно два operator<< для этого:

  1. Один для boost::dynamic_bitset<>, поскольку это BoolTable для любого экземпляра MyClass.
  2. Еще один, шаблонный, для std::vector<T>, так как это MsgTable для каждого соответствующего MyClass<T>,

Недостатком является то, что с этим operator<<, вы сможете вывести любой std::vector<FooBar>, даже если FooBar совершенно не связано с любым использованием MyClass. Но это относится к любой другой возможной реализации надлежащего operator<<s - если нет явного ограничения на параметр MSG, нет ограничения на создание FooBar std::vector<FooBar> жизнеспособный MyClass<MSG>::MsgTable,

Мой вывод на ваш вопрос: вы хотели иметь operator<< для его удобного взгляда, так как он обычно используется для этих целей. В вашем случае вы можете предоставить его для MyClass<MSG> объекты, но нет способа сделать это только для внутренних typedefs.

Я бы реализовал это так:

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

Я полагаю, вы что-то путаете. Типовое имя просто для того, чтобы иметь возможность отделить его от других параметров шаблона. Попробуйте переименовать его в

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

а затем использовать его как объект.

Смотрите здесь.

Другие вопросы по тегам