1

ライブラリ内のstd::stringのコンテナに汎用の行ベースのIOを提供したいと思います。文字列にはスペースが含まれる場合があるため、行ベース。次のコードは正常に機能しているように見えますが、これが最善の方法であるかどうか、またはそれがいくつかのあいまいさを生み出すかどうかはわかりません。私は理解できていません。

#define boostForeach BOOST_FOREACH

template< template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container >
std::ostream& operator<< (std::ostream& o, Container<std::string>const & container){
  boostForeach(std::string const& str, container) {
    o << str << "\n";
  }
  return o;
}

template< template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container >
std::istream& operator>> (std::istream& in, Container<std::string>& container){
  container.clear();
  std::string buf;
  while(getline(in, buf)) {
    if(buf.empty()) break; //stop if empty line found to separate map from other data
    container.insert(container.end(),buf);
  }
  return in;
}

だから問題は:これは安全で健全ですか?

4

2 に答える 2

2

最初の問題は、標準コンテナー内のテンプレート引数の数が標準で義務付けられていないことです。つまり、余分な引数があるプラットフォームではコードが失敗します。

一般に、定義していない型、特に標準コンテナーに対しては、演算子のオーバーロードを提供しません。ちゃんとできないからです。特に、コンテナーがある名前空間でそれらを宣言することはできません。つまり、ADL は正しいオーバーロードを選択するという魔法のようなことをしません。ヘルパー関数が必要な場合は、名前付き関数を提供する必要があります。必要に応じてルックアップを支援する方が簡単です。

これを試して:

#include <iostream>
#include <vector>
#include <string>

template < template<typename,typename> class Container >
std::ostream& operator<<( std::ostream& o, Container<std::string> const & c );
namespace A {
   struct S {};
   std::ostream& operator<<( std::ostream& o, S const & );
   void f() {
      std::vector<std::string> v;
      std::cout << v;
   }
}
int main() {
   A::f();
}
于 2012-07-16T22:01:22.797 に答える
2

次を使用して出力アルゴリズムを記述できますstd::copy()

std::copy(container.begin(),
    container.end(),
    std::ostream_iterator<std::string>(o, "\n"));

段落入力、つまり空白行で区切られた複数行に入力イテレータを使用できます。

class istream_paragraph_iterator: public std::iterator<std::forward_iterator_tag,std::string>{
    std::istream* stream;
    std::string line;
public:

    istream_paragraph_iterator() : stream(0) {}

    istream_paragraph_iterator(std::istream& stream) : stream(&stream) {++*this; //get the first element
    }

    std::string operator*() const {
        return line;
    }

    const std::string* operator->() const {
        return &line;
    }

    istream_paragraph_iterator& operator++() {
        if (stream && (!std::getline(*stream, line) || line.empty()))
            stream = 0;
        return *this;
    }

    istream_paragraph_iterator operator++(int) {
        istream_paragraph_iterator previous(*this);
        ++*this;
        return previous;
    }

    bool operator==(const istream_paragraph_iterator& other) const {
        return stream == other.stream;
    }

    bool operator!=(const istream_paragraph_iterator& other) const {
        return !(*this == other);
    }

};

次に、以下を使用して入力アルゴリズムを記述することもできますstd::copy()

std::copy(istream_paragraph_iterator(in),
    istream_paragraph_iterator(),
    std::back_inserter(container));

ロジックをイテレータ型に分離すると、入力アルゴリズムと出力アルゴリズムをパラメータ化できるようになり、より一般的になります。テンプレート ライブラリでは、これは通常良いことです。標準コンテナーにオーバーロードを追加することは避けたいと思います。なぜなら、それらがすべてのプラットフォームで正しいことを行うかどうかを知ることはできないからです。イテレータベースのアルゴリズムはより移植性が高く、面倒なことをすべて書く必要はありませんtemplate<template<...> class ...>

于 2012-07-16T22:02:32.780 に答える