c++ – Improving constexpr invoke function C++17, alternative to std::invoke

I’ve learned that in C++17, std::invoke isn’t constexpr. To make a constexpr version, I could copy the implementation provided here: https://en.cppreference.com/w/cpp/utility/functional/invoke , OR I can complicate things by making my own version that relies on SFINAE, which may potentially be slow or have errors (which hopefully you’ll help me fix).

Below is my version:

template<std::size_t N> struct theCharArray{
  unsigned char theChar(N);  
};

template<class T> struct hasType;

template<class T, class U, class... Args> auto overloadInvoke(T f, U t1, Args&&... args)
-> theCharArray<(sizeof( hasType<decltype((static_cast<U&&>(t1).*f )(static_cast<Args&&>(args)...))>* ),1 )>;

template<class T, class U, class... Args> auto overloadInvoke(T f, U t1, Args&&... args)
-> theCharArray<(sizeof( hasType<decltype((t1.get().*f)(static_cast<Args&&>(args)...))>* ),2 )>;

template<class T, class U, class... Args> auto overloadInvoke(T f, U t1, Args&&... args)
-> theCharArray<(sizeof( hasType<decltype(((*static_cast<U&&>(t1)).*f)(static_cast<Args&&>(args)...))>* ),3 )>;

template<class T, class U> auto overloadInvoke(T f, U t1)
-> theCharArray<(sizeof( hasType<decltype(static_cast<U&&>(t1).*f)>* ),4 )>;

template<class T, class U> auto overloadInvoke(T f, U t1)
-> theCharArray<(sizeof( hasType<decltype(t1.get().*f)>* ),5 )>;

template<class T, class U> auto overloadInvoke(T f, U t1)
-> theCharArray<(sizeof( hasType<decltype((*static_cast<U&&>(t1)).*f)>* ),6 )>;

template <class T, class Type, class T1, class... Args>
constexpr decltype(auto) invoke(Type T::* f, T1&& t1, Args&&... args){
    constexpr auto chooseFxn = sizeof(overloadInvoke(f, t1, static_cast<Args&&>(args)...));
    if constexpr(chooseFxn == 1){
        return (static_cast<T1&&>(t1).*f )(static_cast<Args&&>(args)...);
    } else if constexpr(chooseFxn == 2){
        return (t1.get().*f)(static_cast<Args&&>(args)...);
    } else if constexpr(chooseFxn == 3){
        return ((*static_cast<T1&&>(t1)).*f)(static_cast<Args&&>(args)...);
    } else if constexpr(chooseFxn == 4){
        return static_cast<T1&&>(t1).*f;
    } else if constexpr(chooseFxn == 5){
        return t1.get().*f;
    } else {
        return (*static_cast<T1&&>(t1)).*f;
    }
}

template< class F, class... Args>
constexpr decltype(auto) invoke(F&& f, Args&&... args){
    return static_cast<F&&>(f)(static_cast<Args&&>(args)...);
}

I test the code here. How the SFINAE works is it checks all possible ways to invoke the arguments, and returns a struct whose size is determined by which invocation is well-formed, which I use to take the size of.