基于lambda arity的专门功能模板

Specializing function template based on lambda arity

本文关键字:功能 lambda arity 基于      更新时间:2023-10-16

我正在尝试根据我作为参数传递给它的lambda的ARITY来专业化模板的功能。这就是我为解决方案提出的:

template<typename Function, bool>
struct helper;
template<typename Function>
struct helper<Function, false>
{
    auto operator()(Function&& func)
    {
        std::cout << "Called 2 argument version.n";
        return func(1, 2);
    }
};
template<typename Function>
struct helper<Function, true>
{
    auto operator()(Function&& func)
    {
        std::cout << "Called 3 argument version.n";
        return func(1, 2, 3);
    }
};
template<typename T>
struct B
{
    T a;
    const T someVal() const { return a; }
};
template<typename Function, typename T>
auto higherOrderFun(Function&& func, const T& a)
{
    return helper<Function, std::is_invocable<Function, decltype(a.someVal()), decltype(a.someVal()), decltype(a.someVal())>::value>{}(std::forward<Function>(func));
}

int main()
{
    B<int> b;
    std::cout << higherOrderFun([](auto x, auto y) {return x+y; }, b) << "n";
    std::cout << higherOrderFun([](auto x, auto y, auto z) {return x + y+z; }, b) << "n";
    return 0;
}

有没有办法以更优雅的方式实现这一目标?我仔细研究了这一点:通用lambda的arity

但是,最新的解决方案(Florestan's(将所有参数变成了aribtrary_t,因此必须将它们放回每个Lambda的内部,我认为这并不理想。理想情况下,我希望与Sfinae直接专门使用模板的higherOrderFun,但是由于我使用助手类来实现这一目标。有一种更直截了当的方式吗?例如,不依赖helper类直接将Sfinae直接应用于higherOrderFun?这重点是不必将higherOrderFun更改为higherOrderFun2higherOrderFun3,而是要使编译器从Lambda和给定参数(const T& a(中推导出正确的专业化。

我应该提到我也不关心该函数的参数类型 - 仅是他们的计数,所以如果可能的话,我会将decltype(a.someVal())更改为auto(也许有一种方法可以绕过明确定义类型?(。

以下模板为我提供了lambda, std::function或普通函数指针的参数数。这似乎涵盖了所有基础知识。因此,您专注于n_lambda_parameters<T>::n,然后将其插入模板中。根据您的特定用例,您可能需要使用std::remove_reference_tstd::decay_t提供的设施来包装此。

用G 测试9.需要std::void_t从C 17中,可以在其他地方找到大量模拟std::void_t PRE C 17的示例...

#include <functional>
// Plain function pointer.
template<typename T> struct n_func_parameters;
template<typename T, typename ...Args>
struct n_func_parameters<T(Args...)> {
    static constexpr size_t n=sizeof...(Args);
};
// Helper wrapper to tease out lambda operator()'s type.
// Tease out closure's operator()...
template<typename T, typename> struct n_extract_callable_parameters;
// ... Non-mutable closure
template<typename T, typename ret, typename ...Args>
struct n_extract_callable_parameters<T, ret (T::*)(Args...) const> {
    static constexpr size_t n=sizeof...(Args);
};
// ... Mutable closure
template<typename T, typename ret, typename ...Args>
struct n_extract_callable_parameters<T, ret (T::*)(Args...)> {
    static constexpr size_t n=sizeof...(Args);
};
// Handle closures, SFINAE fallback to plain function pointers.
template<typename T, typename=void> struct n_lambda_parameters
    : n_func_parameters<T> {};
template<typename T>
struct n_lambda_parameters<T, std::void_t<decltype(&T::operator())>>
    : n_extract_callable_parameters<T, decltype(&T::operator())> {};

#include <iostream>
void foo(int, char, double=0)
{
}
int main()
{
    auto closure=
        [](int x, int y)
    // With or without mutable, here.
        {
        };
    std::cout << n_lambda_parameters<decltype(closure)>::n
          << std::endl; // Prints 2.
    std::cout << n_lambda_parameters<decltype(foo)>::n
          << std::endl; // Prints 3.
    std::cout << n_lambda_parameters<std::function<void (int)>>::n
          << std::endl; // Prints 1.
    return 0;
}

我将使用不同的过载:

template<typename Function>
auto higherOrderFun(Function&& func)
-> decltype(std::forward<Function>(func)(1, 2, 3))
{
    return std::forward<Function>(func)(1, 2, 3);
}
template<typename Function>
auto higherOrderFun(Function&& func)
-> decltype(std::forward<Function>(func)(1, 2))
{
    return std::forward<Function>(func)(1, 2);
}

可能优先级为

 struct low_priority {};
 struct high_priority : low_priority{};
template<typename Function>
auto higherOrderFunImpl(Function&& func, low_priority)
-> decltype(std::forward<Function>(func)(1, 2))
{
    return std::forward<Function>(func)(1, 2);
}
template<typename Function>
auto higherOrderFunImpl(Function&& func, high_priority)
-> decltype(std::forward<Function>(func)(1, 2))
{
    return std::forward<Function>(func)(1, 2);
}
template<typename Function>
auto higherOrderFun(Function&& func)
-> decltype(higherOrderFun(std::forward<Function>(func), high_priority{}))
{
    return higherOrderFun(std::forward<Function>(func), high_priority{});
}

如果您想使用Florestan的Arity特征,则可能会导致:

template<typename F>
decltype(auto) higherOrderFun(F&& func)
{
    if constexpr (arity_v<std::decay_t<F>, MaxArity> == 3)
    {
        return std::forward<F>(func)(1, 2, 3);
    }
    else if constexpr (arity_v<std::decay_t<F>, MaxArity> == 2)
    {
        return std::forward<F>(func)(1, 2);
    }
    // ...
}