Home > Enterprise >  No matching function for lambda with reference argument
No matching function for lambda with reference argument

Time:01-10

My MWE are the following two files (main.cpp and main.h):

#include "main.h"

int main(int argc, char *argv[]) {
        ThreadMap M;

        M.forEach([&](std::pair<int, int>& Pair) {
                // ...
        });
}
#ifndef MAIN_H
#define MAIN_H

#include <map>
#include <mutex>
#include <shared_mutex>
#include <thread>

class ThreadMap {
        std::map<int, int> Map;
        std::shared_mutex Mutex;
public:
        using iterator = std::map<int, int>::iterator;

        ThreadMap() = default;

        iterator begin() {
                return Map.begin();
        }

        iterator end() {
                return Map.end();
        }

        template <typename Func>
        void forEach(Func &&func) {
                std::unique_lock<std::shared_mutex> Lock(Mutex);
                for (auto& Element : Map) {
                        std::forward<Func>(func)(Element);
                }
        }
};

#endif /* MAIN_H */

Testing it requires C 17:

clang -O2 -c main.cpp -o main.o -std=c  17

I want to pass a callback function to my class that wraps an STL container with synchronization primitives (the code is shortened dramatically, but the error is the same). Since I want to iterate over every element in the container by reference, I figured that my lambda function also needs to match the reference argument with std::pair<int, int>&. However, compiling gives me:

In file included from main.cpp:1:
./main.h:29:4: error: no matching function for call to object of type '(lambda at main.cpp:6:12)'
                        std::forward<Func>(func)(Element);
                        ^~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:6:4: note: in instantiation of function template specialization 'ThreadMap::forEach<(lambda at main.cpp:6:12)>' requested here
        M.forEach([&](std::pair<int, int>& Pair) {
          ^
main.cpp:6:12: note: candidate function not viable: no known conversion from 'std::pair<const int, int>' to 'std::pair<int, int> &' for 1st argument
        M.forEach([&](std::pair<int, int>& Pair) {
                  ^
1 error generated.

As soon as I remove the & in the lambda function for my argument (-> std::pair<int, int>), it compiles without any problems. I do not want my loop to generate copies of the elements in the container when iterating, so I believed that the & has to stay in the lambda function argument. What did I miss here?

CodePudding user response:

I figured that my lambda function also needs to match the reference argument with std::pair<int, int>&

According to cppref, the value type of std::map<Key, T> is std::pair<const Key, T>. Therefore, you need to pass a function that accepts arguments of the type that can be implicitly converted to its value type to forEach.

If you want to pass the elements by reference, use std::pair<const int, int>&,

        M.forEach([&](std::pair<const int, int>& Pair) {
                // ...
        });

Demo

As to why remove the reference work, std::pair<const int, int> is implicitly converted to std::pair<int, int>. It's calling the 5th constructor listed here.

  • Related