【问题标题】:Injecting lambda as parameter to template method将 lambda 作为参数注入模板方法
【发布时间】:2018-01-31 11:25:22
【问题描述】:

我正在尝试实现类似于boost::static_visitor 的东西,具有接受 lambda 并支持以下 API 的模板函数:

int i1 = Apply([](int i) { return i; }); // doesn't compile
int i2 = Apply([]() { return 10; });     // ok
bool b1 = Apply([]() { return true; });  // ok
Apply([]() { return; });                 // ok

问题是this topic 的延续。 实现

template <typename Function, typename Return = std::result_of_t<Function()>,
    typename = typename std::enable_if<!std::is_same<Return, void>::value>::type>
Return Apply(Function func)
{
    std::cout << "invoked via Return(*)(...)" << std::endl;
    return func();
}

template <typename Function, typename Return = std::result_of_t<Function()>, 
    typename = typename std::enable_if<std::is_same<Return, void>::value>::type>
void Apply(Function func)
{
    std::cout << "invoked via void(*)(...)" << std::endl;
    func();
}

如果 lambda 没有参数也可以正常工作

#include <functional>
#include <type_traits>
#include <iostream>

template <typename Function, typename Return = std::result_of_t<Function()>, typename = typename std::enable_if<!std::is_same<Return, void>::value>::type>
Return Apply(Function func)
{
    std::cout << "invoked via Return(*)(...)" << std::endl;
    return func();
}

template <typename Function, typename Return = std::result_of_t<Function()>, typename = typename std::enable_if<std::is_same<Return, void>::value>::type>
void Apply(Function func)
{
    std::cout << "invoked via void(*)(...)" << std::endl;
    func();
}

int main()
{
    int i1 = Apply([]() { return 10; });
    bool b1 = Apply([]() { return true; });
    Apply([]() { return; });

    std::cout << i1 << " " << b1 << std::endl;
    return 0;
}

但如果 lambda 有参数,std::result_of_t&lt; Function() &gt; 需要传递参数列表来推断 lambda 的结果类型(例如std::result_of_t&lt; Function(int) &gt;)。

【问题讨论】:

  • 只是眯着眼睛我在想可变参数模板。请发一个minimal reproducible example,在无参数情况下编译运行。
  • 问题中的链接是禁止的。
  • @JiveDadson:这不是真的。但是,您的编辑是正确的——不要链接到小代码 sn-ps;它们属于内联。
  • 嘿,@drus,给答案打一个绿色复选标记怎么样?
  • Jive,恐怕没有一个解决方案能满足我的需要。我暂时停止使用没有参数的 lambda。

标签: c++ templates lambda sfinae


【解决方案1】:

一刀切。 :-)

template <typename Function, typename... Args>
decltype(auto) Apply(Function&& func, Args&&... args) {
    std::cout << "invoked" << std::endl;
    return std::forward<Function>(func)(std::forward<Args>(args)...);
}

int main()
{
    int i1 = Apply([]() { return 10; });
    bool b1 = Apply([]( bool b)  { return !b; }, true);
    Apply([]() { return; });
    std::cout << i1 << " " << b1  << std::endl;
    return 0;
}

或者,更好的是,只需使用std::invoke()

【讨论】:

  • 我相信你应该通过Args&amp;&amp; ... args 使Args... args 参数包通用引用,以防止在这些参数不是临时参数时额外复制
  • @Howard -- 完成。我做对了吗?如果没有,请编辑。
  • 差不多。您应该返回decltype(auto),因为没有它,如果func 这样做,您将不会返回引用。另外,有些人也喜欢转发func,因为调用运算符也可以有 ref 限定符,但这非常罕见,所以你并不需要。 (另外:模板隐含inline
【解决方案2】:

您可以使用帮助类来获取 lambda 的返回类型。

template<typename T>
struct ReturnVal {};

template<typename ReturnType, typename Class, typename... Args>
struct ReturnVal<ReturnType(Class::*)(Args...)> {
    using type = ReturnType;
};

template<typename ReturnType, typename Class, typename... Args>
struct ReturnVal<ReturnType(Class::*)(Args...) const> {
    using type = ReturnType;
};

然后您可以通过传递decltypelambdas operator() 来使用它。

template<typename Func>
void someTemplateFunction(Func func) {
    using Return = typename ReturnVal<decltype(&Func::operator())>::type;
}

通过这种方法,您还可以将函数指针传递给ReturnVal。您也可以使用它来提取参数类型。

【讨论】:

  • 这不适用于通用 lambda(或任何手动定义的带有模板 operator() 或多个 operator() 的函子),但 Jive 的回答可以。
猜你喜欢
  • 1970-01-01
  • 2017-06-14
  • 1970-01-01
  • 1970-01-01
  • 2016-04-24
  • 2014-11-16
  • 1970-01-01
  • 2021-12-23
  • 2021-10-03
相关资源
最近更新 更多