SFINAE,如果实现了运算符,则调用函子

问题描述 投票:0回答:1

我有一个带有许多函子的std::tuple,该函子使用不同的参数实现回调。我想在编译时遍历元组并执行与参数兼容的函子:

假设我有一组函子:

struct functor_a { void operator()(const class_x&) {} };
struct functor_b { void operator()(const class_x&) {} void operator()(const class_y&) {} };
struct functor_c { void operator()(const class_x&) {} void operator()(const class_z&) {} };

给出一个包含它们的元组:

using tuple_type = std::tuple<function_a, function_b, function_c>;

我想做类似的事情:

struct executor {

    template<typename message_type, typename... Type>
    static constexpr void exec(std::tuple<Types...>& tup, const message_type& msg) {
        exec(std::index_sequence_for<Types...>(), tup. msg);
    }

    template<typename message_type, std::size_t... Is>
    static void exec(std::index_sequence<Is...>, tuple_type& tup, const message_type& msg) {
        if constexpr (std::is_nothrow_invocable_v<typename std::tuple_element<Is, tuple_type>::type..., message_type>) {
            std::invoke(std::get<Is>(tup)..., msg);
        }
    }
}
};

为了执行此操作(或使用std :: invoke):

auto tup = get_tuple();
executor::exec(tup, class_x()); // Call functor a, b, c
executor::exec(tup, class_y()); // Call functor b
executor::exec(tup, class_z()); // Call functor c

我的constexpr条件一直存在一些问题,该条件始终被评估为true,并且代码无法编译,因为它找不到具有给定参数的运算符:

std::is_nothrow_invocable_v<typename std::tuple_element<Is, tuple_type>::type..., message_type>
c++ c++17 sfinae
1个回答
0
投票

您不再需要SFINAE了,实际上只需要一个简单的if constexpr

#include <tuple>
#include <string>

struct A {
    void operator()(int) const;
};

struct B {
    void operator()(std::string) const;
};

struct C {
    void operator()(int) const;
    void operator()(std::string) const;
};


template<typename FuncT, typename ArgT>
void dispatch_if_possible(const FuncT& f, const ArgT& a ) {
    if constexpr(std::is_invocable_v<FuncT, ArgT>) {
        f(a);
    } 
}


template<std::size_t I=0, typename ArgT, typename... Ts>
void dispatch(const std::tuple<Ts...>& tup, const ArgT& arg) {
  dispatch_if_possible(std::get<I>(tup), arg);

  if constexpr( I+1 < sizeof...(Ts)) {
      dispatch<I+1>(tup, arg);
  }
}


void foo() {
    std::tuple<A, B, C> x;

    dispatch(x, 2);
}

void bar() {
    std::tuple<A, B, C> x;

    dispatch(x, "hello");
}

您可以看到正确的调度here, on godbolt

© www.soinside.com 2019 - 2024. All rights reserved.