应用一组N个函数的第一个有效函数

Vin*_*ent 7 c++ templates overloading template-meta-programming c++14

此前的答案显示了如何根据呼叫的有效性应用功能:此处.但是,它适用于两个功能.N在C++ 14中,我想知道这个概念是否可以推广到使用智能模板编程技巧的函数.

问题如下:

template <std::size_t N, class... X>
/* [Return type] */ apply_on_validity(X&&... x)
{
    // Tricks here
}

// The function would be equivalent to a hypothetical
// template <std::size_t N, class... F, class... Args>
// [Return type] apply_on_validity(F&&... f, Args&&... args)
// where N = sizeof...(F) is the number of functions
Run Code Online (Sandbox Code Playgroud)

在执行方面:

apply_on_validity<3>(f1, f2, f3, a, b, c, d, e);
Run Code Online (Sandbox Code Playgroud)

将:

  • f1(a, b, c, d, e)如果表达式有效则调用,否则调用
  • f2(a, b, c, d, e)如果表达式有效则调用,否则调用
  • f3(a, b, c, d, e)如果表达式有效则调用,否则调用
  • 没做什么

并且在所有情况下,它将返回已执行函数的结果.

在调用端,template参数3指示已指定的函数数.

它在C++ 14中是否可行,如果是这样,怎么样?

Pio*_*cki 8

:

#include <tuple>
#include <utility>
#include <type_traits>
#include <cstddef>

template <std::size_t N, std::size_t... Js, typename Args>
auto apply_on_validity_impl(int, std::integral_constant<std::size_t, N>, std::index_sequence<Js...>, Args&& args)
{
    // Nothing here
}

template <std::size_t N, std::size_t I, std::size_t... Js, typename Args>
auto apply_on_validity_impl(int, std::integral_constant<std::size_t, I>, std::index_sequence<Js...>, Args&& args)
    -> decltype(std::get<I>(std::forward<Args>(args))(std::get<Js + N>(std::forward<Args>(args))...))
{
    return std::get<I>(std::forward<Args>(args))(std::get<Js + N>(std::forward<Args>(args))...);
}

template <std::size_t N, std::size_t I, std::size_t... Js, typename Args>
decltype(auto) apply_on_validity_impl(char, std::integral_constant<std::size_t, I>, std::index_sequence<Js...> seq, Args&& args)
{
    return apply_on_validity_impl<N>(0, std::integral_constant<std::size_t, I + 1>{}, seq, std::forward<Args>(args));
}

template <std::size_t N, typename... Args>
decltype(auto) apply_on_validity(Args&&... args)
{
    return apply_on_validity_impl<N>(0, std::integral_constant<std::size_t, 0>{}, std::make_index_sequence<sizeof...(Args) - N>{}, std::forward_as_tuple(std::forward<Args>(args)...));
}
Run Code Online (Sandbox Code Playgroud)

DEMO

:

#include <tuple>
#include <utility>
#include <type_traits>
#include <cstddef>

template <std::size_t N, std::size_t I, std::size_t... Js, typename Args>
decltype(auto) apply_on_validity_impl(std::index_sequence<Js...> seq, Args&& args)
{        
    if constexpr (I == N)
    {
    }
    else if constexpr (std::is_invocable_v<std::tuple_element_t<I, Args>, std::tuple_element_t<Js + N, Args>...>)
    {
        return std::get<I>(std::forward<Args>(args))(std::get<Js + N>(std::forward<Args>(args))...);
    }
    else
    {
        return apply_on_validity_impl<N, I + 1>(seq, std::forward<Args>(args));
    }
}

template <std::size_t N, typename... Args>
decltype(auto) apply_on_validity(Args&&... args)
{
    return apply_on_validity_impl<N, 0>(std::make_index_sequence<sizeof...(Args) - N>{}, std::forward_as_tuple(std::forward<Args>(args)...));
}
Run Code Online (Sandbox Code Playgroud)

演示2


Jus*_*tin 5

您使用的语法有点尴尬,因为您必须确切知道您拥有多少函数.结合Piotr Skotnicki的解决方案,解决了这个问题:

// Would be a local class except for the fact that it needs
// to have templates, thus can't be local to the function.
template<class... Fs>
class apply_first_f final {
public:
    apply_first_f(Fs&&... fs)
        : fs_{ std::forward<Fs>(fs)... }
    {}

    template<class... Args>
    decltype(auto) operator()(Args&&... args) const
    {
        return apply_impl<sizeof...(Args)>(std::make_index_sequence<sizeof...(Fs)>{}, std::forward_as_tuple(std::forward<Args>(args)...));
    }
private:
    std::tuple<std::decay_t<Fs>...> fs_;

    template<size_t argsSize, size_t... Is, class Args>
    decltype(auto) apply_impl(std::index_sequence<Is...>, Args&& args) const
    {
        return apply_on_validity_impl<sizeof...(Fs)>(
            0,
            std::integral_constant<size_t, 0>{},
            std::make_index_sequence<argsSize>{},
            std::tuple_cat(
                std::forward_as_tuple(std::get<Is>(fs_)...),
                std::forward<Args>(args)
            )
        );
    }
};

template<class... Fs>
auto make_apply_first_valid_f(Fs&&... fs)
{
    return apply_first_f<Fs...>{ std::forward<Fs>(fs)... };
}
Run Code Online (Sandbox Code Playgroud)

功能可以这样使用:

make_apply_first_valid_f(f1, f2, f3)(args);
Run Code Online (Sandbox Code Playgroud)

DEMO(改编自Piotr的演示)

将它与Piotr的C++ 1z示例一起使用只需要进行一些小改动:

template<class... Fs>
class apply_first_f final {
    // ...
    template<size_t argsSize, size_t... Is, class Args>
    decltype(auto) apply_impl(std::index_sequence<Is...>, Args&& args) const
    {
        return apply_on_validity_impl<sizeof...(Fs), /* added */ 0>(
            /* 0, */
            /* std::integral_constant<size_t, 0>{}, */
            std::make_index_sequence<argsSize>{},
            std::tuple_cat(
                std::forward_as_tuple(std::get<Is>(fs_)...),
                std::forward<Args>(args)
            )
        );
    }
    // ...
};
Run Code Online (Sandbox Code Playgroud)


Bar*_*rry 5

这是另一个有趣的踢,滥用重载决议.我们要将每个函数转换为另一个函数,该函数接受一个rank参数,将所有函数组合在一起,然后只调用它们.选择将自然从过载集中消失.

我们的排名只是这种类型的阶梯:

template <int I> struct rank : rank<I-1> { };
template <> struct rank<0> { };
Run Code Online (Sandbox Code Playgroud)

我们需要一个功能变换器:

template <class T, class F>
auto prepend_arg(F&& f) {
    return [f=std::forward<F>(f)](T, auto&&... args)
        -> decltype(std::forward<F>(f)(std::forward<decltype(args)>(args)...))
    {
        return std::forward<F>(f)(std::forward<decltype(args)>(args)...);
    };
}
Run Code Online (Sandbox Code Playgroud)

然后:

template <std::size_t N, std::size_t... Is, std::size_t... Js,
    class... X>
decltype(auto) apply_impl(std::index_sequence<Is...>,
                          std::index_sequence<Js...>,
                          X&&... x)
{
    auto all = std::forward_as_tuple(std::forward<X>(x)...);

    return overload(
        // all of our function cases
        prepend_arg<rank<N-Is>>(std::get<Is>(all))...,
        // base case: do nothing
        [](rank<0>, auto&&...) {}
    )(rank<N>{}, std::get<N+Js>(all)...);
//               ^^^^^^^^^^^^^^^^^^^^^^^^
//               pass in all the arguments    
}

template <std::size_t N, class... X>
decltype(auto) apply_on_validity(X&&... x) {
    return apply_impl<N>(
        std::make_index_sequence<N>{},
        std::make_index_sequence<sizeof...(X)-N>{},
        std::forward<X>(x)...);
}
Run Code Online (Sandbox Code Playgroud)

overload()作为练习离开.