5

Why is this ambiguous?

// In class declaration
template<typename T>
class threadsafe_list {
    void for_each(std::function<void(T&)> _consumer);
    void for_each(std::function<void(const T&)> _consumer) const;
}
// Calling for_each
threadsafe_list<bool> list;

std::function<void(const bool&)> print = [](const bool& _bool) {
    std::cout << (_bool ? "True" : "False") << std::endl;
};

// Warning: for_each is ambiguous
list.for_each(print);

// This is OK
list.for_each([](const bool& _bool) { std::cout << (_bool ? "True" : "False") << std::endl; }); 

Why is the above ambigous? One of the for_each is meant for const while the other isn't. Thanks!

Pac0
  • 21,465
  • 8
  • 65
  • 74
Type Definition
  • 75
  • 1
  • 13
  • This might help: https://stackoverflow.com/questions/1726740/c-error-operator-2-overloads-have-similar-conversions – jjramsey Dec 14 '20 at 14:00
  • 1
    Reopened. This has **nothing to do with lambdas**. It's simply about overload resolution for a function that takes two arguments. – Pete Becker Dec 14 '20 at 14:43

2 Answers2

7

There are two possible overload resolutions for the first call, each involving a conversion:

  1. Choose the first overload, convert std::function<void(const bool&)>std::function<void(bool&)>
  2. Choose the second overload, convert threadsafe_list<bool>&const threadsafe_list<bool>&

Neither conversion is better than the other because they apply to different arguments.

For the second call, one is better than the other because the worse one converts both arguments (the lambda to std::function and list to const threadsafe_list<bool>&).

To resolve this, you can add yet another overload which would be a better match than either of the original two:

void for_each(std::function<void(T&)> _consumer) const;

or, alternatively, just remove std::function completely to ensure an exact match of the argument:

template <typename F>
void for_each(F _consumer);
template <typename F>
void for_each(F _consumer) const;
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
2

The reason there is ambiguity in the first case is that list isn't const, so the implicit argument that becomes this has a qualification conversion, and the other overload has a std::function<void(const bool&)> to std::function<void(bool&)> conversion.

The other case isn't ambiguous because there is a conversion for both arguments in the const case, but only for _consumer in the mutable case.

Caleth
  • 52,200
  • 2
  • 44
  • 75