Reputation: 8434
This is a general, skeleton version of a template problem I'm having in C++. I can't quite figure out how to get the bar
function template to be recognized as a plausible candidate when invoked from foo
.
#include <iostream>
#include <cstdlib>
#include <unordered_map>
template<class T>
std::unordered_map<std::string, T> getStr2TMap() {
return {}; // suppose logic here is a bit more involved
}
template<class T>
std::unordered_map<int, T> getInt2TMap() {
return {}; // suppose logic here is a bit more involved
}
template<class U, class T>
void bar(
const std::function<void (std::unordered_map<U, T>&&)>& baz
) {
if (rand() % 2 > 0) {
baz(getInt2TMap<T>());
} else {
baz(getStr2TMap<T>());
}
}
template<class T>
void foo(
const std::unordered_map<std::string, T>& map1
) {
bar([&map1](auto&& map2) {
// do some things with map1 and map2
});
}
int main() {
std::unordered_map<std::string, int> myMap;
foo<int>(myMap);
}
EDIT
Much simplified version of the code, same error. I'm looking for solutions to the above version though, not this one.
#include <iostream>
#include <functional>
#include <unordered_map>
template<class T>
void foo(
const std::function<void (std::unordered_map<int, T>&&)>& bar
) {
std::unordered_map<int, T> myMap;
bar(myMap);
}
int main() {
foo([](auto&& m) {
});
}
Upvotes: 3
Views: 773
Reputation: 19916
Sam makes a good observation about how U
is inconsistent in bar
. But in your simple example, why go through all the trouble of const std::function<void (std::unordered_map<int, T>&&)>&
when you could write:
#include <iostream>
#include <functional>
#include <unordered_map>
template<class T, class Func>
void foo(
Func bar
) {
std::unordered_map<int, T> myMap;
bar(myMap);
}
int main() {
// needs the hint for T, since it isn't used anywhere
foo<int>([](auto&& m) {
});
}
Upvotes: 1
Reputation: 118340
The shown code attempts to deduce T
and U
for the following type
std::function<void (std::unordered_map<U, T>&&)>
The deduction attempt is for the lambda parameter passed to the template function:
[&map1](auto&& map2) {}
The problem is that a lambda is not a std::function
of some kind. It is a:
...temporary object of unique unnamed non-union non-aggregate class type, known as "closure" type,...
(Cite)
Put in another way, a lambda object is an instance of a class with an operator()
that executes the lambda code (and captured objects get transmogrified into members of the unnamed class). As such, since it is not a std::function
, it is not possible to deduce std::function
's types from it.
Since it is a callable type, it can be converted to a std::function
, though:
bar(static_cast<std::function<void(std::unordered_map<std::string, T> &&)>>
([&map1](auto&& map2) {
// do some things with map1 and map2
}));
}
That'll get the bar()
template function recognized.
But there's still a second problem with the shown code:
if (rand() % 2 > 0) {
baz(getInt2TMap<T>());
} else {
baz(getStr2TMap<T>());
}
Depending on the roll of the dice, the code will attempt to pass either an unordered map of strings, or an unordered map of ints to baz()
.
That's ...not going to work. At this stage of the game, baz
is a std::function
of some kind. It's not a template. As such, it can only take a parameter of one type.
If you add that static_cast
, and make bar()
a:
baz(getStr2TMap<T>());
to match the fact that the caller is passing an unordered map of strings, the resulting code should compile.
What's happening inside your bar()
is a separate issue. Using the static_cast
answers the question of how to get the candidate template bar
recognized.
Upvotes: 3