如何使用自定义标志编写ostream运算符

时间:2013-08-08 18:32:42

标签: c++ io stream operator-overloading

我经常想把一个stl容器写入ostream。 以下代码工作正常(至少对于向量和列表):

template< typename T ,template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container >
std::ostream& operator<< (std::ostream& o, Container<T>const & container){
  typename Container<T>::const_iterator beg = container.begin();
  while(beg != container.end()){
    o << *beg++;
    if (beg!=container.end())  o << "\t";
  }
  return o;
}

现在我想扩展此代码以支持可自定义的分隔符。以下方法显然不起作用,因为操作符应该只接受两个参数。

template< typename T ,template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container >
std::ostream& operator<< (std::ostream& o, Container<T>const & container,char* separator){
  typename Container<T>::const_iterator beg = container.begin();
  while(beg != container.end()){
    o << *beg++;
    if (beg!=container.end())  o << separator;
  }
  return o;
}

如果不诉诸单身人士或全球变数,可以实现这样的目标吗?

理想情况是引入自定义标志或流操纵器,例如std::fixed。然后一个人可以写

std::cout << streamflags::tabbed << myContainer;

2 个答案:

答案 0 :(得分:4)

你可以编写自己的操纵器。 basic_ostream提供operator<< overloads that take a function pointer(请参阅链接中的(9)),并调用该函数将流本身作为参数传递。所以操纵器只是一个函数名。

或者,操纵器可以是具有合适operator<<的对象。这样你就可以写cout << setSeparator(',') << myContainer;了。在这里,setSeparator是一个带有setSeparator(char)构造函数的类。

最后,ios_base提供了xallociwordpword成员,它们基本上允许用户将任意信息与特定的流实例相关联。这就是你的操纵者可以与你的operator<<(Container)进行通信的方式。您需要一个全局变量来存储由xalloc为您分配的索引。

答案 1 :(得分:0)

完成@ Igor-Tandetnik答案,一个简单的(不是线程安全的)更明确的例子:

#include <iostream>             
#include <vector>       

namespace MyNamespace
{
  namespace IO
  {
    enum class OrientationEnum
    {
      Row,
      Column
    };

    struct State
    {
      OrientationEnum orientation = OrientationEnum::Row;
    };

    static State currentState;

    template <typename CharT, typename Traits>
    inline std::basic_ostream<CharT, Traits>& rowOriented(
        std::basic_ostream<CharT, Traits>& os)
    {
      currentState.orientation = OrientationEnum::Row;

      return os;
    }

    template <typename CharT, typename Traits>
    inline std::basic_ostream<CharT, Traits>& columnOriented(
        std::basic_ostream<CharT, Traits>& os)
    {
      currentState.orientation = OrientationEnum::Column;

      return os;
    }
  }

  template <typename T>
  std::ostream& operator<<(std::ostream& out, const std::vector<T>& toPrint)
  {
    switch (IO::currentState.orientation)
    {
      case IO::OrientationEnum::Column:
        for (const auto& e : toPrint)
        {
          out << e << "\n";
        }
        break;

      case IO::OrientationEnum::Row:
        for (const auto& e : toPrint)
        {
          out << e << " ";
        }
        break;

      default:
        break;
    }

    return out;
  }
}

//////////////////////////////////////////////////////////////////

using namespace MyNamespace;

int main()
{
  std::vector<int> v(5,0); // A 5-vector of 0 

  // If you want to save your state
  // auto savedState = IO::currentState;

  std::cout << "\nBy row\n"
            << IO::rowOriented << v
            //
            << "\nBy column\n"
            << IO::columnOriented << v;

  // IO::currentState = savedState;
}

您可以编译并运行它。

g++ streamExample.cpp -o streamExample
./streamExample

输出结果为:

By row
0 0 0 0 0 
By column
0
0
0
0
0