7

Assuming two overloads:

void X::f(int, float);
void X::f(long, double);

is it possible, in C++17 or higher, to infer int/long (or whatever it is) from float/double for the second argument?

To be used in code like this:

using Arg1Type = arg_type_t<1, X::f, float>;
Arg1Type value;

2 Answers 2

12

For overloaded functions, template argument deduction works by performing trial deduction against each overload, and ignoring failed deductions ([temp.deduct.call]/6). So we can create a function template such that deduction succeeds only if the argument is the interesting overload, and make the function template return the interesting type.

template<class T>
struct select {
    template<class U, class Ret>
    static U infer(Ret (U, T));
};

Now, the instantiation of select<float> produces a member function template such that the template arguments can be deduced from void (int, float), but not from void (long, double) (due to mismatch between float and double).

    template<class U, class Ret>
    static U infer(Ret (U, float));

Then the parameter type of f can be acquired by applying decltype to the result of infer(&f).

void f(int, float);
void f(long, double);

using T1 = decltype(select<float>::infer(&f)); // T1 is int
using T2 = decltype(select<double>::infer(&f)); // T2 is long
Sign up to request clarification or add additional context in comments.

1 Comment

It has to be tweaked for member function though.
3

Actually, the difficult point is to select a given overload among several ones given some criteria, e.g. the presence of a specific type in the parameters. Once the hunted overload is found, one can get all the parameter types in a std::tuple and then select any type at required index.

You can design a select template function that takes as input a member function of class X with its second parameter being of a specific type T

template<class T,class X,class R, class A0, class...Tail>
auto select(R (X::*) (A0, T, Tail...)) -> std::tuple<A0,T,Tail...>;

Here, the second parameter of the input member function is explicitely the hunted type T. From that you can return a std::tuple holding all the parameter types. You will notice how we can pass a member function as parameter with X::* where X can be deduced by a call such as select<float>(&X::f)

The annoying part is that this select function will detect the hunted type only at position 1. If you want to detect this type at other positions, you need, well, to provide overloads of select that will take type T for parameter at different positions, eg.

#include <tuple>

template<class T,class X,class R, class...Tail>
auto select(R (X::*) (T, Tail...)) -> std::tuple<T,Tail...>;

template<class T,class X,class R, class A0, class...Tail>
auto select(R (X::*) (A0, T, Tail...)) -> std::tuple<A0,T,Tail...>;

template<class T,class X,class R, class A0, class A1, class...Tail>
auto select(R (X::*) (A0,A1, T, Tail...)) -> std::tuple<A0,A1,T,Tail...>;

struct MyClass {
    void f(char,float,int)  {}
    void f(long,char, double,int)  {}
};

using t0 = decltype(select<float> (&MyClass::f));
static_assert (std::is_same_v<std::tuple_element_t<0,t0>,char>);
static_assert (std::is_same_v<std::tuple_element_t<2,t0>,int>);

using t1 = decltype(select<double> (&MyClass::f));
static_assert (std::is_same_v<t1,std::tuple<long,char,double,int>>);

int main()  {}

Demo

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.