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