簡體   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