根据表达式的有效性选择要应用的函数

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

问题如下C++14:

  • 我们有两个功能FV&& valid_f,FI&& invalid_f和参数Args&&... args
  • 如果表达式有效,则apply_on_validity应该应用该函数valid_fargsstd::forward<FV>(valid_f)(std::forward<Args>(args)...)
  • 否则,如果std::forward<FV>(invalid_f)(std::forward<Args>(args)...)是有效的表达式,apply_on_validity应适用invalid_fargs
  • 否则apply_on_validity应该什么都不做

我猜代码看起来像这样:

template <class FV, class FI, class... Args, /* Some template metaprog here */>
void apply_on_validity(FV&& valid_f, FI&& invalid_f, Args&&... args)
{
    // Apply valid_f by default
    std::forward<FV>(valid_f)(std::forward<Args>(args)...);
}

template <class FV, class FI, class... Args, /* Some template metaprog here */>
void apply_on_validity(FV&& valid_f, FI&& invalid_f, Args&&... args)
{
    // Apply invalid_f if valid_f does not work
    std::forward<FV>(invalid_f)(std::forward<Args>(args)...);
}

template <class FV, class FI, class... Args, /* Some template metaprog here */>
void apply_on_validity(FV&& valid_f, FI&& invalid_f, Args&&... args)
{
    // Do nothing when neither valid_f nor invalid_f work
}
Run Code Online (Sandbox Code Playgroud)

但我真的不知道该怎么做.任何的想法?


链接到这里的概括.

Pio*_*cki 17

采取:

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

然后:

template <class FV, class FI, class... Args>
auto apply_on_validity_impl(rank<2>, FV&& valid_f, FI&& invalid_f, Args&&... args)
    -> decltype(std::forward<FV>(valid_f)(std::forward<Args>(args)...), void())
{
    std::forward<FV>(valid_f)(std::forward<Args>(args)...);
}

template <class FV, class FI, class... Args>
auto apply_on_validity_impl(rank<1>, FV&& valid_f, FI&& invalid_f, Args&&... args)
    -> decltype(std::forward<FI>(invalid_f)(std::forward<Args>(args)...), void())
{
    std::forward<FI>(invalid_f)(std::forward<Args>(args)...);
}

template <class FV, class FI, class... Args>
void apply_on_validity_impl(rank<0>, FV&& valid_f, FI&& invalid_f, Args&&... args)
{

}

template <class FV, class FI, class... Args>
void apply_on_validity(FV&& valid_f, FI&& invalid_f, Args&&... args)
{
    return apply_on_validity_impl(rank<2>{}, std::forward<FV>(valid_f), std::forward<FI>(invalid_f), std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

DEMO


And*_*dyG 11

Piotr Skotnicki的答案非常棒,但是这样的代码让我觉得有必要指出C++ 17会有多清洁,这要归功于constexpr if其他类型的特性is_callable:Demo Demo *这个版本会产生更多警告,但更简单

template <class FV, class FI, class... Args>
void apply_on_validity(FV&& valid_f, FI&& invalid_f, Args&&... args)
{
    if constexpr (std::is_callable_v<FV(Args...)>)
        std::cout << "Apply valid_f by default\n";
    else
    {
        if constexpr (std::is_callable_v<FI(Args...)>)
            std::cout << "Apply invalid_f if valid_f does not work\n";
        else
            std::cout << "Do nothing when neither valid_f nor invalid_f work\n";
    }
}
Run Code Online (Sandbox Code Playgroud)


Bar*_*rry 7

这是一个替代的答案,只是为了踢.我们需要一个static_if:

template <class T, class F> T&& static_if(std::true_type, T&& t, F&& ) { return std::forward<T>(t); }
template <class T, class F> F&& static_if(std::false_type, T&& , F&& f) { return std::forward<F>(f); }
Run Code Online (Sandbox Code Playgroud)

还有一个is_callable.由于您只是支持功能,我们可以这样做:

template <class Sig, class = void>
struct is_callable : std::false_type { };

template <class F, class... Args>
struct is_callable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>>
: std::true_type
{ };
Run Code Online (Sandbox Code Playgroud)

然后我们可以构建逻辑:

template <class FV, class FI, class... Args>
void apply_on_validity(FV&& valid_f, FI&& invalid_f, Args&&... args)
{
    auto noop = [](auto&&...) {};

    static_if(
        is_callable<FV&&(Args&&...)>{},
        std::forward<FV>(valid_f),
        static_if(
            std::is_callable<FI&&(Args&&...)>{},
            std::forward<FI>(invalid_f),
            noop
        )
    )(std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)