help me!! std::function and lambdas

I'm trying to experiment w/ lambdas and std::function

What this program do (supposed to do) is simply print the contents of a container
using a specified pattern, (i'm going to use it for file i/o) but i'ts having some errors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>
#include <vector>
#include <functional>
using namespace std;

struct User {
    string username;
    string pass;
};

template <class Iterator>
void print( Iterator first, Iterator last,
            std::function <std::string( Iterator )> pattern )
{
    while( first++ != last )
        cout << pattern( first ) << endl;
}

int main ()
{
    vector<User> arrs( 10, {"shadow", "fiend"} );
    
    auto pattern = []( User* user )
    { return user->username + ' ' + user->pass; };
    
    print( arrs.begin(), arrs.end(), pattern );
}


errors

main.cpp: In function 'int main()':
main.cpp:27:46: error: no matching function for call to 'print(std::vector<User>::iterator, std::vector<User>::iterator, main()::__lambda0&)'
print( arrs.begin(), arrs.end(), pattern );
^
main.cpp:27:46: note: candidate is:
main.cpp:13:6: note: template<class Iterator> void print(Iterator, Iterator, std::function<std::basic_string<char>(Iterator)>)
void print( Iterator first, Iterator last,
^
main.cpp:13:6: note: template argument deduction/substitution failed:
main.cpp:27:46: note: 'main()::__lambda0' is not derived from 'std::function<std::basic_string<char>(Iterator)>'
print( arrs.begin(), arrs.end(), pattern );
^
Last edited on
It looks to me like your lambda function type doesn't match the one your print() function expects.

std::function <std::string( User* )> is the type of the lambda
std::function <std::string( vector<User>::Iterator )> is the type expected by print given the values of arrs.begin() and arrs.end()

I'd just make the pattern lambda take a vector iterator instead of a pointer to a user.
Ok, i've tried that but it's still giving errors :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>
#include <vector>
#include <functional>
using namespace std;

struct User {
    string username;
    string pass;
};

template <class Iterator>
void print( Iterator first, Iterator last,
            std::function <std::string( Iterator )> pattern )
{
    while( first++ != last )
        cout << pattern( first ) << endl;
}

int main ()
{
    std::vector<User> arrs( 10, {"shadow", "fiend"} );
    
    auto pattern = []( std::vector<User>::iterator itr )
    { return itr->username + ' ' + itr->pass; };
    
    print( arrs.begin(), arrs.end(), pattern );
}


the errors are

main.cpp: In function 'int main()':
main.cpp:27:46: error: no matching function for call to 'print(std::vector<User>::iterator, std::vector<User>::iterator, main()::__lambda0&)'
print( arrs.begin(), arrs.end(), pattern );
^
main.cpp:27:46: note: candidate is:
main.cpp:13:6: note: template<class Iterator> void print(Iterator, Iterator, std::function<std::basic_string<char>(Iterator)>)
void print( Iterator first, Iterator last,
^
main.cpp:13:6: note: template argument deduction/substitution failed:
main.cpp:27:46: note: 'main()::__lambda0' is not derived from 'std::function<std::basic_string<char>(Iterator)>'
print( arrs.begin(), arrs.end(), pattern );
^
Maybe JLBorges or Cubbi could explain why this doesn't work.
I've seen this problem before, and the easy solution (which might disappoint you) is this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <string>
#include <vector>
#include <functional>
using namespace std;

struct User {
    string username;
    string pass;
};

template <class Iterator, class Function>
void print( Iterator first, Iterator last, Function pattern )
{
    while( first != last )
        cout << pattern( first++ ) << endl;
}

int main ()
{
    std::vector<User> arrs( 10, {"shadow", "fiend"} );

    auto pattern = []( std::vector<User>::iterator itr ) -> std::string
    { return itr->username + ' ' + itr->pass; };

    print( arrs.begin(), arrs.end(), pattern );
}
clang's error message has a helpful note:
test.cc:13:6: note: candidate template ignored: could not match 'function<std::__1::basic_string<char, std::__1::char_traits<char>,
      std::__1::allocator<char> > (type-parameter-0-0)>' against '<lambda at test.cc:24:20>'
void print( Iterator first, Iterator last,
     ^


the "why" here is that template argument deduction never performs type conversion (give or take things like const): the compiler is trying to come up with a type to substitute for "Iterator" in the template parameter list such that the function parameter type "std::function <std::string( Iterator )>" becomes exactly the same type as the type of the argument you're calling it with (the lambda's type), and that can't happen.

Either make functor a template parameter as Catfish did (it's the usual approach)

or skip the deduction:

print<std::vector<User>::iterator>( arrs.begin(), arrs.end(), pattern );
Last edited on
oh, yeah, since you have enough arguments to deduce Iterator already, you have the option of excluding pattern from deduction:

print(arrs. begin(), arrs. end(), {pattern} ) ;
oh, yeah, since you have enough arguments to deduce Iterator already, you have the option of excluding pattern from deduction:

print(arrs. begin(), arrs. end(), {pattern} ) ;

I'm not the OP, but on his behalf I'll ask:

1) for which version of print() did you write this call?
2) what exactly does the initializer list(?) do here, again?
the version that takes a std::function
brace-init list has no type and does not participate in the deduction, it's a bit of a language hack i guess. There are other ways to exclude arguments from deduction, I couldn't think of another simple one that fits here
Last edited on
Unless the function explicitly takes a std::initializer_list<> as the parameter, a brace-enclosed-initializer-list argument is considered in a non-deduced context.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <initializer_list>

template < typename T > void foo( std::initializer_list<T> arg ) {}

template < typename T > void bar( T arg ) {}

int main ()
{
    foo( {1} ) ; // fine, {1} is a brace-enclosed-initializer-list;
                 // foo has an std::initializer_list<> parameter
                 // T is deduced to be int; call foo( std::initializer_list<int> )

    bar( std::initializer_list<int>{1} ) ; // fine; bar( std::initializer_list<int> )
                                           // with T == std::initializer_list<int>

    bar( {1} ) ; // *** error, bar does not have an initializer list parameter
                 // the brace-enclosed-initializer-list {1} is used in a non-deduced context
                 // note: bar( std::initializer_list<int> ) with T == std::initializer_list<int>
                 // is not considered for argument deduction
}
Thanks everyone, i'll consider using catfish's approach
Topic archived. No new replies allowed.