2017-02-25 38 views
3

我認爲operator<<的調用會產生一個雙參數函數調用。那麼,爲什麼不編譯呢?如何爲ostream創建一個lambda?

#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 
    } 
}; 

錯誤的是(GCC-6)

$ 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_); 
          ^

我也試過電話os << w2(os,c.h_)但gcc和我都認爲是無稽之談。此外,我嘗試儘可能自動拉姆達:

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

也沒有運氣。

任何提示?

+1

您只將一個參數傳遞給需要兩個參數的lambda表達式。你也將你的lambda的返回值傳遞給'operator <<'這是一個'std :: ostream&'。 – 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<<的通話將產生兩個參數的函數調用。

沒有,調用一個重載operator<<基本上是一樣調用一個二元函數:

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

什麼你正在試圖做的使用返回ostream&作爲右拉姆達被調用operator<<但是你沒有把lambda本身的參數傳遞給參數。


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到lambda表達式。

+0

當然...是的,我經常使用函數調用ish' <<'來使用陷阱。謝謝,明白了。我不認爲這裏需要完美捕捉:我們不在模板中,我們確切知道我們有什麼左值和右值。據我所知,不需要摺疊。 – towi

+0

@towi:是的,這裏不需要。我指的是'make_streamable'的全面實現。 –