Overload ambiguity with std :: function, which takes the std :: optional parameter

Here is a simple sample program:

using fn_string = function<void(const string&)>; using fn_optional_string = function<void(const optional<string>&)>; void foo(fn_string) { cout << "string" << endl; } void foo(fn_optional_string) { cout << "optional string" << endl; } int main() { foo([&](const string&){ }); foo([&](const optional<string>&){ }); // <-- ambiguous return 0; } 

It has 2 overloads for foo() - one of which performs a function with the string parameter, and the other with optional<string> .

Why is the 2nd call to foo() ambiguous?

Is there an easy way to fix it? No shots?

UPDATE

The above was a simplified example of the following real-world problem that I am trying to solve:

 using delegate = variant< function<void()>, function<void(const string&)>, function<void(const optional<string>&)> >; struct foo { void add_delegate(delegate fn) { fns.push_back(std::move(fn)); } vector<delegate> fns; }; int main() { foo bar; bar.add_delegate([&](){ }); bar.add_delegate([&](const string&){ }); bar.add_delegate([&](const optional<string>&){ }); // ERROR return 0; } 

The last call to add_delegate will not compile, since it cannot solve between function<void(const string&)> and function<void(const optional<string>&)> .

I realized that the problem is with overload resolution (hence my original example). What change should be made to add_delegate to allow it to accept all 3 versions of lambdas?

A complete example can be found at Coliru .

+5
source share
2 answers

Lambda is not std::function<> . A std::function<R(Args...)> is the value type of the erase type that can be stored by any object that is compatible with the R(Args...) code R(Args...)

In one case, above R there is void (which for a std::function means "I donโ€™t care what it returns), and Args... is std::string . The called object is compatible with the call with this, if you can call its with std::string rvalue.

This applies to both std::optional<std::string> and std::string .

There is no special overload for โ€œexact matchingโ€ - everything that matters is compatible with the call or not.

There are several ways to handle this.

 template<std::size_t N> struct overload_order : overload_order<N-1> {}; template<> struct overload_order<0> {}; namespace details { void foo(overload_order<1>, fn_string) { cout << "string" << endl; } void foo(overload_order<0>, fn_optional_string) { cout << "optional string" << endl; } } template<class F> void foo(F&& f) { foo( overload_order<!std::is_same<std::decay_t<F>, fn_optional_string>{}>{}, std::forward<F>(f) ); } 

now we will first try fn_string alone, and only if that fails, try fn_optional_string if the argument is no longer fn_optional_string , in which case we send directly to this overload.

+4
source

Declare the argument specifically as fun_optional_string.

I do not know what to type so that the software does not complain only about the code, but the poem:

There is an old hack from Milpitas ... His motto is: โ€œNo mistake can defeat usโ€ .... His decision never expires ... When he shoots at these synapses ... Fueled by donuts, cold rice and fajit ... .

 #include <functional> #include <iostream> #include <optional> #include <string> using namespace std; using fn_string = function<void(const string&)>; using fn_optional_string = function<void(const optional<string>&)>; void foo(fn_string) { cout << "string" << endl; } void foo(fn_optional_string) { cout << "optional string" << endl; } int main() { foo([&](const string&){ }); fn_optional_string g = [&](const optional<string>&) {}; foo(g); // <-- not ambiguous return 0; } 
+1
source

Source: https://habr.com/ru/post/1276174/


All Articles