2017-02-25 8 views
3

私はoperator<<の呼び出しが2パラメータの関数呼び出しを生成すると考えました。だから、なぜこれはコンパイルされないのですか?ostream用のラムダを作成するには?

#include <iostream> // ostream 
#include <iomanip> // setw, setfill 
using std::ostream; using std::setw; using std::setfill; 
struct Clock { 
    int h_, m_, s_; 
    Clock(int hours, int minutes, int seconds) 
    : h_{hours}, m_{minutes}, s_{seconds} {} 
    void setClock(int hours, int minutes, int seconds) { 
     h_ = hours; m_ = minutes; s_ = seconds; 
    } 
    friend ostream& operator<<(ostream&os, const Clock& c) { 
     auto w2 = [](ostream&os, int f) -> ostream& { 
      return os << setw(2) << setfill('0') << f; }; 
     return os << w2(c.h_) <<':'<<w2(c.m_)<<':'<<w2(c.s_); // ERROR 
    } 
}; 

エラーは、私は、コールos << w2(os,c.h_)なくGCCを試み、私はナンセンスた合意

$ g++-6 -std=gnu++1y ... 
file.cpp: In function ‘std::ostream& operator<<(std::ostream&, const Clock&)’: 
file.cpp:745:33: error: no match for call to ‘(operator<<(std::ostream&, const Clock&)::<lambda(std::ostream&, int)>) (const int&)’ 
     return os << w2(c.h_) <<':'<<w2(c.m_)<<':'<<w2(c.s_); 
          ^

(GCC-6)です。また、ラムダを可能な限り自動で試しました。

auto w2 = [](auto&os, auto f) { 
    return os << setw(2) << setfill('0') << f; }; 

でも運がありません。

ヒント

+1

2つのパラメータを必要とするラムダに1つのパラメータのみを渡しています。また、ラムダの戻り値を 'std :: ostream&'である 'operator <<'に渡しています。 – Galik

答えて

2

これは、コンパイルされます。

friend ostream& operator<<(ostream&os, const Clock& c) { 
    auto w2 = [](ostream&os, int f) -> ostream& { 
     return os << setw(2) << setfill('0') << f; }; 
    return w2(os, c.h_); 
} 
+0

...出力は '23:59:59'ではありません。 – towi

4

私はoperator<<の呼び出しは2パラメータの関数呼び出しを生成するだろうと思いました。

いいえ、過負荷にoperator<<は基本的にバイナリの関数を呼び出すことと同じです呼び出す:

a << b; 
// is equivalent to 
operator<<(a, b); 
// or to 
a.operator<<(b); 

あなたが右のようにostream&を返すラムダを使用してoperator<<を起動さやろうとしていますostream&引数をラムダそのものに渡すわけではありません。


os << w2(os,c.h_)構文的に有効ですが、何のoperator<<(ostream&, ostream&)定義が存在しないため、コンパイルされません。あなたは、単にそれをストリーミングすることなく、ラムダを呼び出すされて何ができるか

friend ostream& operator<<(ostream&os, const Clock& c) { 
    auto w2 = [](ostream&os, int f) -> ostream& { 
     return os << setw(2) << setfill('0') << f; }; 

    w2(os, c.h_); 
    os <<':'; 
    w2(os, c.m_); 
    os << ':'; 
    return w2(os, c.s_); 
} 

wandbox example


ご希望の構文を実現したい場合は、もう少し作業が必要になります。

template <typename TF> 
struct streamable : TF 
{ 
    streamable(TF&& f) : TF{std::move(f)} { } 
}; 

template <typename TF> 
auto& operator<<(ostream& os, const streamable<TF>& s) 
{ 
    s(os); return os; 
} 

template <typename TF> 
auto make_streamable_impl(TF f) 
{ 
    return streamable<TF>(std::move(f)); 
} 

template <typename TF> 
auto make_streamable(TF f) 
{ 
    return [&](auto&& x) mutable 
    { 
     return make_streamable_impl([&](ostream& os) -> auto& 
     { 
      f(os, x); 
      return os; 
     }); 
    }; 
} 

使用法:

friend ostream& operator<<(ostream&os, const Clock& c) { 
    auto w2 = make_streamable([](ostream&os, int f) -> ostream& { 
     return os << setw(2) << setfill('0') << f; }); 
    return os << w2(c.h_) <<':'<<w2(c.m_)<<':'<<w2(c.s_); 
} 

wandbox example

注意実際の実装は、おそらくperfectly-capture the argumentsラムダに必要があることをここで可能な解決策です。

+0

もちろん...私はしばしばそのコールトラップをfunction-call-ish '<< 'で使用します。ありがとう、それを得た。私はここで完璧なキャプチャが必要とは思わない:私たちはテンプレートではない、私たちは私たちが持っているlvaluesとrvaluesを正確に知っている。私が見る限り、虚脱は必要ありません。 – towi

+0

@towi:はい、ここでは必要ありません。私はmake_streamableの完全な一般的な実装を指していました。 –

関連する問題