This is a follow up of this question: Why can I call a callable that is const-referenced and where the actual callable is a mutable lambda?
I can mimic the implicit conversion of a const mutable closure to function pointer when it's called like this:
#include <iostream>
void dummy() { std::cout << "call dummy\n";}
using fptr = void(*)();
struct callable {
void operator()() {std::cout << "call callable\n"; }
operator fptr() const {
std::cout << "convert\n";
return &dummy;
}
};
int main()
{
auto exec = []( auto const &fn ) {
std::cout << "call\n";
fn();
};
exec( callable{} );
}
I do not understand why the implicit conversion is triggered. It does not take place when passing the argument (the argument is deduced, hence does not take into account implicit conversion) but only when () is invoked as evident from the output:
call
convert
call dummy
I tried to experiment to understand what is going on. Trying something similar with a different operator does not work (as expected):
#include <iostream>
struct bar {
void operator+(int) const { std::cout << "bar\n"; };
};
struct foo {
void operator+(int) { std::cout << "foo\n"; }
operator bar() const { return {}; }
};
int main() {
auto exec = []( auto const &fn ) {
std::cout << "call\n";
fn + 42;
};
exec(foo{});
};
<source>:15:12: error: passing 'const foo' as 'this' argument discards qualifiers [-fpermissive]
15 | fn + 42;
| ~~~^~~~
What is special about the call operator? How is it possible that fn() implicitly converts fn to call something else?
PS: Meanwhile I discovered that it must be related to conversion to function pointer, because when I turn the first example to something more similar than the second I get the expected error:
#include <iostream>
struct foo {
void operator()() const { std::cout << "call foo\n"; }
};
struct callable {
void operator()() {std::cout << "call callable\n"; }
operator foo() const { return {}; };
};
int main()
{
auto exec = []( auto const &fn ) {
std::cout << "call\n";
fn();
};
exec( callable{} );
}
Now I get the expected error
<source>:17:11: error: no match for call to '(const callable) ()'
17 | fn();
| ~~^~
Now the question is: What is special about conversions to function pointer type?
It appears that fn() takes into account conversions to function pointer. Why?
intthen it will be implicitly used in the+case as well.+case also has some special case defined for conversions to built-in integral types, but I'm not going to go digging for that one right now.