Search code examples
c++lambdac++20

Input/output manipulators and lambda capturing


I have the following code that compiles and works correctly due to such a concept as Input/output manipulators:

#include <iostream>
#include <ostream>

struct st{
auto mke(){
    return [](std::ostream& os) -> decltype(auto) {return os<<42;};
}
};

int main(){
    std::cout<<st{}.mke();
}

But actually, in my project, I need to capture this by reference and output some fields of the structure. Something like this:

#include <iostream>
#include <ostream>

struct st{
int a=42;
auto mke(){
    return [this](std::ostream& os) -> decltype(auto) {return os<<a;};
}
};

int main(){
    std::cout<<st{}.mke();
}

But it doesn't compile:

error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'st::mke()::<lambda(std::ostream&)>')
   12 |     std::cout<<st{}.mke();

Q: I need a detailed explanation of the reasons preventing the compilation of the second example. What really happened?


Solution

  • Your first example works because the standard operator<< accepts a function pointer, and your non-capturing lambda is convertible to such a type.

    Your second example fails because a capturing lambda is not convertible to a function pointer.

    The typical way to solve this is to not use a lambda at all. Return a struct/class object instead, and overload operator<< to handle that type, eg:

    #include <iostream>
    #include <ostream>
    
    struct st {
        int a = 42;
    
        struct manipulator {
            st& m_st;
        };
    
        manipulator mke() {
            return {*this};
        }
    };
    
    std::ostream& operator<<(std::ostream& os, const st::manipulator &io) {
        return os << io.m_st.a;
    }
    
    int main(){
        std::cout << st{}.mke();
    }
    

    Online Demo