【问题标题】:How to create a lambda for ostream?如何为 ostream 创建 lambda?
【发布时间】:2017-07-16 08:00:43
【问题描述】:

我认为operator<< 的调用会生成一个双参数函数调用。那么,为什么这不能编译呢?

#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 &lt;&lt; w2(os,c.h_),但 gcc 和我都同意这是无稽之谈。我也尽可能自动地尝试了 lambda:

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

也没有运气。

有什么提示吗?

【问题讨论】:

  • 您只是将一个参数传递给需要两个参数的 lambda。此外,您将 lambda 的返回值传递给 operator&lt;&lt;,这是一个 std::ostream&amp;

标签: c++ lambda operator-overloading outputstream ostream


【解决方案1】:

这样编译:

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

【讨论】:

    【解决方案2】:

    我认为operator&lt;&lt; 的调用会生成一个双参数函数调用。

    不,调用重载的operator&lt;&lt; 与调用二进制函数基本相同:

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

    您要做的是使用返回 ostream&amp; 作为右手参数的 lambda 调用 operator&lt;&lt;,但您没有将 ostream&amp; 参数传递给 lambda 本身。


    os &lt;&lt; w2(os,c.h_) 在语法上有效,但不会编译,因为没有 operator&lt;&lt;(ostream&amp;, ostream&amp;) 定义。

    您可以做的只是简单地调用 lambda 而不用流式传输它:

    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。

    【讨论】:

    • 当然...是的,我经常使用函数调用&lt;&lt; 来击中那个陷阱。谢谢,明白了。我认为这里不需要完美的捕获:我们不在模板中,我们确切地知道我们拥有哪些左值和右值。据我所知,不需要 ref-collapse。
    • @towi:是的,这里不需要。我指的是make_streamable 的完全通用实现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-16
    • 2020-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多