4

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

2 回答 2

4

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

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

最后,提供ios_base和成员,它们本质上允许将任意信息与特定流实例相关联。这就是您的操纵器可以与您的. 您确实需要一个全局变量来存储由.xallociwordpwordoperator<<(Container)xalloc

于 2013-08-08T18:44:10.413 回答
1

为了完成@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
于 2017-08-22T14:03:50.693 回答