繁体   English   中英

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

[英]How to write an ostream operator with custom flags

我经常想将 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;

您可以编写自己的操纵器。 basic_ostream提供接受函数指针的operator<<重载(请参阅链接中的 (9)),并调用该函数并将流本身作为参数传递。 所以操纵器只是一个函数名。

或者,操纵器可以是具有合适operator<<的对象。 这将允许您编写cout << setSeparator(',') << myContainer; . 这里, setSeparator是一个带有setSeparator(char)构造函数的类。

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

为了完成@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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM