So I had this scenario where I want to call either function of a class, where the function in question has the same prototype but it's also overloaded. Since I know of pointer to members my immediate reaction was something like this:
struct test
{
int overloaded(char) {}
int overloaded(int) {}
int overloadedone(char) {}
int overloadedone(int) {}
} test;
int main()
{
(test.*(true ? (&test::overloaded) : (&test::overloadedone)))(1);
}
However it turned out the compiler (MSVC - 2019 Preview latest version with std C++ preview) can't deduce the type and I have to write:
(test.*(true ? static_cast<int (test::*)(int)>(&test::overloaded) : static_cast<int (test::*)(int)>(&test::overloadedone)))(1);
instead which made me return to the good old:
true ? test.overloaded(1) : test.overloadedone(1);
But I wonder if this is the defined behavior of requiring those cast. Even:
(test.*static_cast<int (test::*)(int)>(true ? (&test::overloaded) : (&test::overloadedone)))(1);
Doesn't work.
You have to write said cast on each of the two possibilities for the ternary as in the second example.
It isn't particularly elegant, but this approach can deduce an overload if you curry the member function pointer's arguments before passing the member function pointers themselves:
#include <iostream>
template <class... Args>
auto invoke_conditional_mem_fn(Args... args)
{
return [=] <class R, class X> (X x, bool b, R(X::*t)(Args...), R(X::*f)(Args...)) -> R
{
return (x.*(b ? t : f))(args...);
};
}
struct test
{
int overloaded(char) { std::cout << "overloaded(char) "; return 1; }
int overloaded(int) { std::cout << "overloaded(int) "; return 2; }
int overloadedone(char) { std::cout << "overloadedone(char) "; return 3; }
int overloadedone(int) { std::cout << "overloadedone(int) "; return 4; }
} test;
int main()
{
std::cout
<< invoke_conditional_mem_fn('1')(test, true, &test::overloaded, &test::overloadedone)
<< std::endl
<< invoke_conditional_mem_fn(1)(test, false, &test::overloaded, &test::overloadedone)
<< std::endl;
}
Thanks to @dyp and their example, we know that we can infer the return type and base of the member function pointers if we select which arguments to pass.
Alternatively, you could do something a little simpler like this, if it meets your needs. Just declare a lambda to work around the limitations of your ternary expression with an if
and else
statement since each branch of a ternary operator is required to be of the same type.
#include <iostream>
struct test
{
int overloaded(char) { std::cout << "overloaded(char) "; return 1; }
int overloaded(int) { std::cout << "overloaded(int) "; return 2; }
int overloadedone(char) { std::cout << "overloadedone(char) "; return 3; }
int overloadedone(int) { std::cout << "overloadedone(int) "; return 4; }
} test;
auto conditional = [] (struct test& test, bool cond, auto... args)
{
if (cond) return test.overloaded(args...);
else return test.overloadedone(args...);
};
int main()
{
std::cout << conditional(test, true, '1') << std::endl;
std::cout << conditional(test, false, 1) << std::endl;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With