3

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) {
  });
}
1
  • It looks like part of the problem here is that I need to explicitly pass a type arg when I call foo? Is there any way to deduce it? Commented Dec 10, 2016 at 2:56

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

the rand() thing was my attempt to approximate more involved logic where I don't actually know what the key type is. In reality not going to have to handle 2 different key types, just one unknown one. I guess I goofed on simplifying it here.
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.