Alex Aparin
Alex Aparin

Reputation: 4512

How to pass operator << for string as parameter?

I want to do something like that:

vector<string> road_map;
// do some stuff
for_each(road_map.begin(), road_map.end(), bind(cout, &ostream::operator<<));

Note: I don't want to use lambda for this purpose, like that:

[](const string& str){ cout << str << endl; }

C++ compiler will create aux code for lambda. That's why I don't want to use lambda in such case. I suppose that there is more lightweight solution for such problem. Of course it is not critical, if there is not simple solution I just will use lambda.

Upvotes: 0

Views: 85

Answers (2)

PeterT
PeterT

Reputation: 8284

This answer is mainly used to investigate the C++ compiler will create aux code for lambda claim.

You should note that there is no member function ostream::operator<< taking a std::string. There's only a free-standing function opertator<<(std::ostream&,const std::string&) defined in the <string> header.

I used gcc godbolt, you can see the example Live here

So I made a version using a lambda and a version using std::bind:

With bind you get

void funcBind(std::vector<std::string>& a) 
{
    using namespace std;
    using func_t = std::ostream&(*)(std::ostream&, const std::string&);
    func_t fptr = &operator<<; //select the right overload
    std::for_each(
        a.begin(),
        a.end(), 
        std::bind(
            fptr,
            std::ref(std::cout),
            std::placeholders::_1)
    );
}

and this assembly on x86_64

        push    rbp
        push    rbx
        sub     rsp, 8
        mov     rbp, QWORD PTR [rdi+8]
        mov     rbx, QWORD PTR [rdi]
        cmp     rbx, rbp
        je      .L22
.L24:
        mov     rdx, QWORD PTR [rbx+8]
        mov     rsi, QWORD PTR [rbx]
        mov     edi, OFFSET FLAT:std::cout
        add     rbx, 32
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        cmp     rbp, rbx
        jne     .L24
.L22:
        add     rsp, 8
        pop     rbx
        pop     rbp
        ret

and with a lambda you get:

void  funcLambda(std::vector<std::string>& a)  
{
  std::for_each(
    a.begin(),
    a.end(),
    [](const std::string& b){std::cout << b;});
}

and this assembly

        push    rbp
        push    rbx
        sub     rsp, 8
        mov     rbp, QWORD PTR [rdi+8]
        mov     rbx, QWORD PTR [rdi]
        cmp     rbx, rbp
        je      .L27
.L29:
        mov     rdx, QWORD PTR [rbx+8]
        mov     rsi, QWORD PTR [rbx]
        mov     edi, OFFSET FLAT:std::cout
        add     rbx, 32
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        cmp     rbp, rbx
        jne     .L29
.L27:
        add     rsp, 8
        pop     rbx
        pop     rbp
        ret

so you don't actually see any difference with any appreciable level of optimization enable

Upvotes: 4

Rudi
Rudi

Reputation: 19940

You can use an ostream_iterator to generate the output.

#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>

int main()
{
    std::vector<std::string> road_map{"ab", "cde"};
    // do some stuff
    std::copy(road_map.begin(), road_map.end(),
              std::ostream_iterator<std::string>(std::cout, "\n"));
}

Upvotes: 2

Related Questions