Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to automatically deduce the type of the pointer to member overloaded function in ternary when called after?

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.

like image 970
AnArrayOfFunctions Avatar asked Jan 02 '21 05:01

AnArrayOfFunctions


1 Answers

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;
}
like image 167
Patrick Roberts Avatar answered Nov 16 '22 19:11

Patrick Roberts