After narrowing it down I just saw there already are two answers. Yet I do write a new answer as it shows an alternative how you can narrow it down with STL type traits, going from the outer to the inner.
#include <type_traits>
#include <typeinfo>
#include <iostream>
class cClass {};
template <typename T>
struct Extract : std::false_type {};
template <typename TRet, typename TArg>
struct Extract<TRet (cClass::*)(TArg) &> : std::true_type {
using Ret = TRet;
using Arg = TArg;
};
int main()
{
using Type = char(&(cClass::* [1])(cClass(*)[2])&)[3];
std::cout << "Type=" << typeid(Type).name() << '\n';
static_assert(std::is_array_v<Type>);
using Type1 = decltype(std::declval<Type>()[0]);
std::cout << "array[" << std::extent_v<Type> << "] of " << typeid(Type1).name() << '\n';
static_assert(std::is_reference_v<Type1>);
using Type2 = decltype(std::remove_reference_t<Type1>());
std::cout << "ref to " << typeid(Type2).name() << '\n';
static_assert(std::is_member_function_pointer_v<Type2>);
std::cout << "member function pointer, cClass, taking " << typeid(Extract<Type2>::Arg).name() << ", returning " << typeid(Extract<Type2>::Ret).name() << '\n';
using FuncParam = cClass(*)[2]; // Pointer to array of 2 cClass
using FuncRet = char(&)[3]; // Reference to array of 3 chars
using Func = FuncRet(cClass::*)(FuncParam) &; // Member function for lvalue of cClass, taking FuncParam, returning FuncRet
using FuncArrayOne = Func[1]; // Array with one Func (WTF?)
static_assert(std::is_same_v<FuncArrayOne, Type>);
return 0;
}
This writes
Type=char (& __ptr64 (__cdecl cClass::*[1])(class cClass (* __ptr64)[2]) __ptr64& )[3]
array[1] of char (& __ptr64 (__cdecl cClass::*)(class cClass (* __ptr64)[2]) __ptr64& )[3]
ref to char (& __ptr64 (__cdecl cClass::*)(class cClass (* __ptr64)[2]) __ptr64& )[3]
member function pointer, cClass, taking class cClass (* __ptr64)[2], returning char [3]
To understand which next function was necessary in each step I output for example:
using Type_x = Type1;
std::cout << std::is_array_v<Type_x> << '\n';
std::cout << std::is_class_v<Type_x> << '\n';
std::cout << std::is_function_v<Type_x> << '\n';
std::cout << std::is_member_function_pointer_v<Type_x> << '\n';
std::cout << std::is_pointer_v<Type_x> << '\n';
std::cout << std::is_reference_v<Type_x> << '\n';
The ones and zeroes give me a hint what to do next.
A tricky part was on the member function pointer. First there is no std type trait to extract the class, the return type and the parameter type(s). And then this was where the & came in place that I failed to understand first. Gladly I knew that you can append & and && to a member function, and the MSVC STL implements _is_member_function_pointer via _Is_memfunptr which gave me an additional hint how and where to add the &.
My code stops here as I now understand all parts. If you wanted to go deeper, you could just repeat the steps.
When going back from inner to outer I added a using declarations in each step, so the code gets much better understandable. Again there was one surprise: the outmost steps suggest that FuncArrayOne should be an array of references to Func, but that is not possible, so it is just an array of Func.
To be sure that the results are correct there is a final static_assert with std::is_same_v.