Я узнал, что в C ++ 17 std :: invoke не constexpr. Чтобы сделать версию constexpr, я мог скопируйте реализацию, представленную здесь: https://en.cppreference.com/w/cpp/utility/functional/invoke , ИЛИ я могу усложнить ситуацию, сделав свою собственную версию, основанную на SFINAE, которая потенциально может быть медленной или содержать ошибки (которые, надеюсь, вы поможете мне исправить).
Ниже моя версия:
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)...);
}
Я тестирую код здесь. Как работает SFINAE, заключается в том, что он проверяет все возможные способы вызова аргументов и возвращает структуру, размер которой определяется тем, какой вызов правильно сформирован, который я использую для получения размера.
Как я тестировал код:
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << 'n'; }
int num_;
};
void print_num(int i)
{
std::cout << i << 'n';
}
struct PrintNum {
void operator()(int i) const
{
std::cout << i << 'n';
}
};
int main()
{
invoke(print_num, -9);
invoke([]() { print_num(42); });
const Foo foo(314159);
invoke(&Foo::print_add, foo, 1);
std::cout << "num_: " << invoke(&Foo::num_, foo) << 'n';
invoke(PrintNum(), 18);
}
