Улучшение функции вызова constexpr C ++ 17, альтернативы std :: invoke

Я узнал, что в 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);
}

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *