Kvass
Kvass

Reputation: 8434

C++ Candidate Template Ignored error when passing lambda as argument for std::function

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

Answers (2)

Alex Reinking
Alex Reinking

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

Sam Varshavchik
Sam Varshavchik

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

Related Questions