2017-02-25 4 views
3

Ich dachte, der Anruf der operator<< würde einen Zwei-Parameter-Funktionsaufruf erzeugen. Also, warum kompiliert das dann nicht?Wie erstelle ich ein Lambda für 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 
    } 
}; 

Der Fehler ist (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_); 
          ^

Ich habe auch versucht, den Anruf os << w2(os,c.h_) aber gcc und ich stimmte das war Unsinn. Auch habe ich versucht das Lambda so auto-wie möglich:

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

auch kein Glück.

Irgendwelche Hinweise?

+1

Sie übergeben nur einen Parameter an Ihr Lambda, der zwei Parameter benötigt. Außerdem übergeben Sie den Rückgabewert Ihres Lambda an 'operator <<' was ein 'std :: ostream &' ist. – Galik

Antwort

2

Dies kompiliert:

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

... aber die Ausgabe ist nicht '23: 59: 59', oder? – towi

4

Ich dachte, der Ruf der operator<< einen Zwei-Parameter-Funktionsaufruf erzeugen würde.

Nein, Berufung auf eine überladene operator<< eine binäre Funktion als Aufruf im Grunde das gleiche ist:

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

Was eine Lambda-operator<< verwenden, die eine ostream& als rechts- zurückgibt aufrufen tun Sie versuchen, Handargument, aber Sie übergeben das Argument ostream& nicht an das Lambda selbst.


os << w2(os,c.h_) ist syntaktisch gültig, aber nicht kompiliert, weil es keine operator<<(ostream&, ostream&) Definition.

Was Sie tun können, ist einfach die Lambda aufrufen, ohne es zu Streaming:

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


Wenn Sie Ihre gewünschte Syntax erreichen wollen, werden Sie ein wenig mehr Arbeit benötigen. Hier ist eine mögliche Lösung:

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

Verbrauch:

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

Hinweis, dass eine wirkliche Umsetzung sollte wohl perfectly-capture the arguments in die Lambda-Ausdrücke.

+0

Natürlich ... Ja, ich traf häufig die Falle mit der Funktion-Call-ish '<<' verwenden. Danke, verstanden. Ich denke nicht, dass eine perfekte Erfassung hier erforderlich wäre: Wir sind nicht in einer Vorlage, wir wissen genau, welche Werte und Werte wir haben. Kein Ref-Collapsing nötig, soweit ich sehen kann. – towi

+0

@towi: Ja, es wird hier nicht benötigt.Ich habe mich auf eine ganz allgemeine Implementierung von 'make_streamable' bezogen. –

Verwandte Themen