Following up my question c++ - How do implicit conversions work when using an intermediate type?, from which I understood the rule of 1 implicit conversion max, I'm trying to understand a more advanced use case involving function arguments.
Let's say I have a function, which accepts as a parameter another function. That function parameter could either return something, or return nothing (void). Therefore, I want to overload the function definition, to accept in one case the non-void function argument, and in the other case the void function argument.
using VoidType = void (string);
using NonVoidType = string (string);
string call(VoidType *fn, string arg) {
cout << "[using void function]" << endl;
fn(arg);
return arg;
}
string call(NonVoidType *fn, string arg) {
cout << "[using non void function]" << endl;
return fn(arg);
}
When calling the function, the given argument has a known type, so the overload selection should be straightforward, such as here:
void printVoid(string message) {
cout << message << endl;
}
string printNonVoid(string message) {
cout << message << endl;
return message;
}
void test() {
call(printVoid, "call(printVoid)");
call(printNonVoid, "call(printNonVoid)");
}
But it's not when I have intermediate types, such as std::function
wrappers:
string call(function<VoidType> fn, string arg) {
cout << "[using void function]" << endl;
fn(arg);
return arg;
}
string call(function<NonVoidType> fn, string arg) {
cout << "[using non void function]" << endl;
return fn(arg);
}
void test() {
call(printVoid, "call(printVoid)");
call(printNonVoid, "call(printNonVoid)"); // more than one instance of overloaded function "call" matches the argument list:C/C++(308)
}
for which I get the error more than one instance of overloaded function "call" matches the argument list:C/C++(308)
on the second call.
A solution to this is to instantiate the std::function
explicitly before calling:
void test() {
call(function(printNonVoid), "call(function(printNonVoid))");
}
But, how is call(function(printNonVoid), "xxx")
so different from call(printNonVoid, "xxx")
knowing that in the latter case I would expect the implicit conversion to do the equivalent to what is done in the former case.
Am I missing something, like some intermediate, hidden copy constructors or whatever?
Full example:
#include <iostream>
using namespace std;
using VoidType = void (string);
using NonVoidType = string (string);
string callFromPointer(VoidType *fn, string arg) {
cout << "[callFromPointer] void" << endl;
fn(arg);
return "<void>";
}
string callFromPointer(NonVoidType *fn, string arg) {
cout << "[callFromPointer] non void" << endl;
return fn(arg);
}
string callFromFunction(function<VoidType> fn, string arg) {
cout << "[callFromFunction] void" << endl;
fn(arg);
return "<void>";
}
string callFromFunction(function<NonVoidType> fn, string arg) {
cout << "[callFromFunction] non void" << endl;
return fn(arg);
}
void printVoid(string message) { cout << "\t[printVoid] " << message << endl; }
string printNonVoid(string message) {
cout << "\t[printNonVoid] " << message << endl;
return message;
}
void sep() { cout << "\n----\n" << endl; }
void main() {
callFromPointer(printVoid, "callFromPointer(printVoid)");
callFromPointer(printNonVoid, "callFromPointer(printNonVoid)");
sep();
callFromFunction(printVoid, "callFromFunction(printVoid)");
callFromFunction(printNonVoid, "callFromFunction(printNonVoid)"); // more than one instance of overloaded function "callFromFunction" matches the argument list:C/C++(308)
sep();
callFromFunction(function(printVoid), "callFromFunction(function(printVoid))");
callFromFunction(function(printNonVoid), "callFromFunction(function(printNonVoid))");
}