2017-02-25 63 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'的全面实现。 –