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.