1

I am training using lambda function and I create a small function that merge two lists using template class. I want to use lambda function in order to compare age and merge them into a new list that will be in ascending order. Compiler tell that no instance of function template "merge_func" matches the argument list. If you can help me understand what is wrong in my lambda function, and how can I correct it.

 template <class T>
list<T*> merge_func(list<T*> first_list, list<T*> second_list, bool(*func)(T x, T y))
{
    list<T> merge_list;
    auto it_first = first_list.begin();
    auto it_second = second_list.begin();
    while (it_first != first_list.end() && it_second != second_list.end())
    {
        if ((*func)(*it_first, *it_second))
        {
            merge_list.push_back((*it_first));
            it_first++;
        }
        else
        {
            merge_list.push_back((*it_second));
            it_second++;
        }
    }
    while (it_first != first_list.end())
    {
        merge_list.push_back((*it_first));
        it_first++;
    }
    while (it_second != second_list.end())
    {
        merge_list.push_back((*it_second));
        it_second++;
    }
    return merge_list;
}

class Student
{
public:
    string _name;
    double _age;
    Student(string name, double age) : _name(name), _age(age) {};
};
int main()
{
    list<Student*> std;
    std.push_back(new Student("Lior", 15.5));
    std.push_back(new Student("Yossi", 60));

    list<Student*> std2;
    std2.push_back(new Student("Arie", 23));
    std2.push_back(new Student("Eli", 80));

    list<Student*> std3;

    std3 = merge_func(std, std2, [](Student* x, Student* y)->bool {return x->_age < y->_age; });
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Ronyco123
  • 87
  • 6
  • 1
    Your list store *pointers* not objects. `list` should be `list`. – Some programmer dude Jul 19 '21 at 09:20
  • Your lists have a template type of `Student*` but your lambda expects `Student` arguments. Those two don't match – UnholySheep Jul 19 '21 at 09:21
  • Your *second* problem is that your callback takes its argument as *objects*, not as pointers. – Some programmer dude Jul 19 '21 at 09:21
  • @Someprogrammerdude Thanks I have correct what you say but still not compile. What do you mean by `callback`. See my code I edit it – Ronyco123 Jul 19 '21 at 09:23
  • The `func` [callback](https://en.wikipedia.org/wiki/Callback_(computer_programming)). I.e. your lambda. Your `merge_func` function is passing *pointers* when it calls `func`, not objects. – Some programmer dude Jul 19 '21 at 09:25
  • @Someprogrammerdude I don't success correct the code. Can you help me edit the code to correct the issue – Ronyco123 Jul 19 '21 at 09:28
  • https://stackoverflow.com/a/22299052/4885321 add +before lambda – alagner Jul 19 '21 at 09:30
  • `*it_first` and `*it_second` are *pointers*. The type of e.g. `*it_first` is `Student*`. Just like you changed the list to use `T*` you must change `func` to use `T*` as its arguments. And that also means you must do the same update to the lambda. – Some programmer dude Jul 19 '21 at 09:30
  • 1
    Please don't change your code after some valid comments or answers were given. This way you make those answers look wrong. This also makes your question unusable to others who may have similar problems and seek a solution for them. If you want to present a fixed code _append_ it to your question below the original one. – CiaPan Jul 19 '21 at 09:33

2 Answers2

2

Two issues.

  1. The element type of the list is Student*, lambda should take Student* too.

  2. You're passing lambda to merge_func which expects a function pointer. Template argument deduction doesn't consider implicit conversion (from lambda to function pointer) and fails deducing template parameter T on the 3rd function parameter.

You can change merge_func to

template <class T>
list<T> merge_func(list<T> first_list, list<T> second_list, bool(*func)(T x, T y))

And convert lambda to function pointer when calling merge_func.

std3 = merge_func(std, std2, static_cast<bool(*)(Student* x, Student* y)>([](Student* x, Student* y)->bool {return x->_age < y->_age; }));

Or perform the conversion with sorcery operator+.

std3 = merge_func(std, std2, +[](Student* x, Student* y)->bool {return x->_age < y->_age; });

LIVE

Or prevent deduction on the 3rd function parameter.

template <class T>
list<T> merge_func(list<T> first_list, list<T> second_list, std::type_identity_t<bool(*)(T x, T y)> func)

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Thanks ! If I understand all the problem is that I use `template` and the compiler doesn't know how to convert lambda to function pointer using template ? (Sorcery operator not work...) – Ronyco123 Jul 19 '21 at 09:49
  • @Ronyco123 Why `operator+` not work? It should be guaranteed. I tried it and works fine. https://wandbox.org/permlink/nNtK4tnV3zMJx2zz – songyuanyao Jul 19 '21 at 09:51
  • I am on Visual Studio and operator+ not work. But the first solution works very well. – Ronyco123 Jul 19 '21 at 09:52
  • @Ronyco123 Got it. It might be just MSVC's bug. – songyuanyao Jul 19 '21 at 09:53
1

On top of @songyuanyao's answer, you can also utilize a more generic version. Then the prototype of merge_func would look like this:

template <class T, class Compare>
list<T> merge_func(list<T> first_list, list<T> second_list, Compare&& comp)

and the if inside it:

if (invoke(std::forward<Compare>(comp),*it_first, *it_second))

Then it will be callable just with the lambda:

std3 = merge_func(std, std2, [](Student* x, Student* y) {return x->_age < y->_age; });

Demo

SFINAE/concept-related code is left for you to write ;)

alagner
  • 3,448
  • 1
  • 13
  • 25