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) {
// ...
});
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.