Reputation: 12299
When trying to compiling this, suprisingly, it gives an error because the auto
parameter of the lambda function has been resolved to std::string
, and the compiler doesn't know how to convert std::string
to int
or Widget
when calling test
.
But, I wonder why the compiler has choosen the second invok
function instead of the first one, when the first one would succeed:
#include <string>
#include <functional>
struct Widget {};
bool test(int );
bool test(Widget );
void invok(std::function<bool(int)> ); // #1
void invok(std::function<bool(std::string)> ); // #2
int main()
{
// error: unresolved overloaded function type
// invok(test);
// still error: no known conversion from std::string to
// int or Widget
invok([](auto&& x) {
return test(std::forward<decltype(x)>(x));
});
}
That example has been copied from a C++ proposal.
Upvotes: 2
Views: 211
Reputation: 275820
Your overload remains ambiguous (sort of), but along the way to determining that your code runs into a hard error when trying out the two possibilities (passing an int or a string).
To see the ambiguity, add a ->bool
to your lambda so it doesn't have to compile the body to determine the return value.
The body of a lambda is not in an area where a subsitution failure results in not an error. Instead you get a hard error there.
The easy fix is to make your lambda take an int explicitly.
If you want a generic solution:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__( decltype(args)(args)... ) )
and then
invok(OVERLOADS_OF(test));
does (at least closer to) the right thing.
This macro moves the failure from the body of the lambda, to a trailing return type. And the failure (that string cannot be passed to test) now occurs in a context where substitution failure causes not an error (SFINAE). So everything works.
The exact rules for what is SFINAE friendly and what is not require reading the standard. However, the rule of thumb is that works well is that compilers don't have to treat errors in bodies of functions as substitution errors, and that accessing the contents of an undefined class is a hard error. The first because it seemed a reasonable place to draw a line and make it easier for compiler writers; the second because the alternative is insanity or ODR bug bait.
In practice, the standards SFINAE rules are more arcane, and last I checked in C++14 there was an omission of SFINAE being required during template class partial specialization: every compiler supported it. But maybe I misread it. In any case, the rule of thumb I use seems just as useful as the standard text. Neither are going to be perfect.
Upvotes: 3
Reputation: 137425
The compiler didn't choose #2. It's trying to decide if it can choose #2.
To do that, it asks "can this generic lambda be converted to std::function<bool(std::string)>
"?
std::function
's converting constructor says "only if it's callable with a std::string
rvalue and the result type is convertible to bool
".
Compiler tries that, deduce auto
as std::string
, substitute into the signature of the function call operator...success! Oops, the return type is auto
, and it needs an actual type to answer the "is convertible" question. So it instantiates the body of the function call operator template to figure out the return type.
Ouch. The body isn't valid for std::string
after all. Hard error and explosions follow.
Upvotes: 3