2017-02-25 1 views
3

operator<<의 호출이 두 개의 매개 변수 함수 호출을 생성한다고 생각했습니다. 그래서, 왜 이것이 컴파일되지 않습니까?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

두 매개 변수가 필요한 매개 변수 하나만 람다에 전달합니다. 또한 람다의 반환 값을'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<<의 호출이 두 매개 변수의 함수 호출을 생성 할 거라 생각 했어요.

아니, 오버로드 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의 완전히 일반적인 구현을 언급하고 있었다. –

관련 문제