2017-01-16 12 views
0

私はこのような一般的な、完全に準拠したIStream「抽出演算子」希望:演算子を実装する方法>>(std :: istream&、std :: array <char, N>&)?

template <typename CharT, size_t N, class Traits> 
std::basic_istream<CharT, Traits>& operator>>(
    std::basic_istream<CharT, Traits>& in, 
    std::array<CharT, N>& out) 
{ 
    std::basic_string<CharT, Traits> buf; // this is not great 
    in >> buf; 
    if (buf.size() >= N) { 
     in.setstate(std::ios::failbit); // is this the right thing to do? 
     out[0] = 0; 
    } else { 
     std::copy(buf.begin(), buf.end(), out.data()); 
     out[buf.size()] = 0; 
    } 

    return in; 
} 

をしかし、これは不必要にメモリを割り当て、コピー。私はそれを避けたいです。しかし、私はまた、適切な空白やstd::noskipwsなどのサポートを含む完全な機能を維持したいと思います.Boostを使用すると、はるかに簡単であれば問題ありません。このような

+1

一度に1文字ずつ読むだけでなく、オーバーフローをチェックするためにカウントを維持するという方法で問題があることがわかったはずです。 – Arunmu

+0

@JohnZwinck私は、あなたが望む正確な振る舞いをいくつかの例を挙げて指定するべきだと思います。 '' a b c "'、 '' abc ''です。 – Holt

+0

@Holt: 'std :: array 'の代わりに 'std :: string'を使用した場合とまったく同じ動作が必要です。文字列がヌルターミネータを含めてNの内側に収まらない場合は、設定の失敗が追加されます。残りの動作とすべてのエッジケースは、通常のC++文字列と同じように処理する必要があります。 –

答えて

0

何か:

#include <array> 
#include <istream> 
#include <sstream> 

template <typename CharT, size_t N, class Traits> 
std::basic_istream<CharT, Traits>& operator>>(
    std::basic_istream<CharT, Traits>& in, 
    std::array<CharT, N>& out) 
{ 
    auto first = out.begin(); 
    auto last = out.end(); 
    while (first != last and in) 
    { 
     in >> *first++; 
    } 
    return in; 
} 

int main() 
{ 
    std::istringstream ss { "a cd" }; 
    ss >> std::noskipws; 

    std::array<char, 4> a; 
    ss >> a; 

    for (char c : a) { 
     std::cout << "[" << c << "]\n"; 
    } 
} 

結果:

[a] 
[ ] 
[c] 
[d] 
+0

ありがとうございます。入力ストリームが空であるか、または失敗した場合、最初の文字としてゼロを配置することを含む、NULL終了を行うことはできますか? –

+0

ここに重要なバグがあります:空白に遭遇したとき(noskipwsなし)に停止しません。通常の 'std :: string'演算子はそうですが、これも同じです。 –

+0

@JohnZwinckあなたが「バグ」と言ったとき、これは、これらの条件に遭遇した時にあなたが何らかの定義された行動をしていたことを意味します。私はこのコードを書くときに、これらの要件を認識していませんでした(ユーザーと実装者の間の永続的な問題があります)。 正式に要件を記述している場合は、テストスイートを構築して検証し、合格するまでこのコードを反復することができます。 それはすべてあなたが欲しい行動を正確に知っていることから始まります。 –

1

ない完全な答えが、コメントには大きすぎる:

のみオペランドnamespace stdであるつながる演算子のオーバーロードトラブルへ。これは二相検索のため、他のカスタムoperator>>はあなたのものを隠すでしょう、see example

通常、この問題は、オペランドの1つと同じ名前空間にオーバーロードされた演算子を定義することによって回避されるため、引数依存ルックアップによって常に演算子が検出されます。 (たとえより近い名前空間に名前があったとしても、ADLは囲み名前空間を検索します)。

しかし、その解決策はご利用いただけません。undefined behaviour to add your own functions to namespace stdです。

私はこの問題に対する推奨される回避策が不明です。テンプレート引数で>>を使用して汎用テンプレートコードでこの関数を見つけたい場合は、同じ関数内にusing ::operator>>;という演算子を呼び出して、明示的にオーバーロードを表示する必要があります。またはもっと細かく、using mystuff::operator>>あなた自身の名前空間にあなたのものを置く場所。

+0

おっと!私の特別なケースでは、自分のネームスペースで 'std :: array'のtypedefまたはwrapperクラスを使いたいと思っています。だから、私はこの問題を回避することができると思いますが、それを指摘してくれてありがとうございます! –

1

次のように動作するはずです:

template <typename CharT, size_t N, class Traits> 
std::basic_istream<CharT, Traits>& operator>>(
    std::basic_istream<CharT, Traits>& in, 
    std::array<CharT, N>& out) 
{ 
    in >> std::setw(N) >> out.data(); 
    if (!in.eof() && !std::isspace((CharT)in.peek(), in.getloc())) { 
     out[0] = 0; 
     in.setstate(in.rdstate() | std::ios::failbit); 
    } 
    return in; 
} 

は、私の知る限り、それは決して超えるN - 1文字を読まないことを除いてstd::basic_stringでご使用のバージョンのように振る舞います。

関連する問題