【问题标题】:Simplest way to pass a lambda as a method parameter in C++17在 C++17 中将 lambda 作为方法参数传递的最简单方法
【发布时间】:2021-02-16 12:40:03
【问题描述】:

Google 向我展示了很多关于将 lambdas 作为参数传递的 SO 帖子,但它们似乎都是基于 C++0x/C++11(例如 Use a lambda as a parameter for a C++ function),我知道这是一个在更新的 C++ 版本?

我想将一个非常简单的委托return MyGlobals.GetX() 作为方法变量传递——在我的例子中,MyGlobals 是一个全局对象。 即在 伪代码

//stores and/or calls the lambda
myObject.SetXGetter({return MyGlobals.GetX()});

MyObject::SetXGetter(lambda?)
{
 this->mLambda = Lambda;
 cout << mLambda();
}

较旧的 C++ 版本使得不得不使用 STL 包装器和临时类型变得非常混乱,所以在 C++17 及更高版本中,它可以做得多么整洁......SetGetter 看起来像什么,可以称为传递直接在 lambda 中?

(如果更新版本有不同的替代方案,请分享)

【问题讨论】:

  • 不就是myObject.SetXGetter( []{ return MyGlobals.GetX(); } );吗?你发现了什么?你尝试了什么?你不喜欢怎么办?在 C++11 改进后,你在哪里读到了这些版本,还有什么需要改进的?这是一个不清楚的问题。
  • @underscore_d 我不知道,我几乎没有使用过 lambda。鉴于我提供了一个确切的例子,这似乎很清楚......我怎样才能最简单地声明和调用SetXGetter?正如我所说,我所找到的只是关于使用 functors 等的旧 C++ 的答案
  • 为我工作。您是直接使用 lambda 还是 std::function
  • @Eljay 什么对你有用,没有人发布有效代码?我不知道你的问题是什么意思,但我想要最接近我的伪代码
  • 也许你把事情搞糊涂了。从来没有必要使用凌乱的包装器和/或“临时类型”来将 lambda 传递给函数。顺便说一句,您链接的问题的答案仍然适用

标签: c++ c++17


【解决方案1】:

一个函数在自己的执行过程中只想调用一个传递的 lambda 或其他仿函数可以是一个函数模板,推断仿函数的类型:

template <typename F>
void use_it(F&& func) {
    do_something_with(func());
}

在这里您要存储稍后调用的函子,因此我们需要一个可以包装各种函子的通用类型。这就是std::function 的用途。假设返回类型为int

#include <functional>

class MyObject {
// ...
private:
    std::function<int()> mLambda;
};
void MyObject::SetXGetter(std::function<int()> func) {
    mLambda = std::move(func);
}

您的调用缺少最初的 [捕获],这是 lambda 表达式所必需的,即使没有捕获任何内容:

myObject.SetXGetter([]{ return MyGlobals.GetX(); });

【讨论】:

    【解决方案2】:

    我知道这是一个在最近的 C++ 版本中有所改进的领域?

    一般不会。它已扩展到一些更复杂的情况,但简单的情况与 C++11 几乎相同。

    在 C++17 中将 lambda 作为方法参数传递的最简单方法

    选项保持不变:

    • 您可以拥有一个模板并按原样传递 lambda。
    • 或者您可以将 lambda 封装在类型擦除包装器中,例如 std::function,在这种情况下,您可以将其传递给非模板函数。
    • 或者,如果 lambda 是非捕获的,则可以将其转换为函数指针,该指针也可以传递给非模板。

    哪个最简单可能因上下文和个人意见而异。函数包装器在最广泛的不同情况下的工作方式相同,这是一个简单的选择。函数指针不涉及动态分配,在低层次上最容易理解。模板不涉及任何类型的包装或转换,但它是一个模板。


    this->mLambda = Lambda;
    

    鉴于您希望将可调用对象存储为可修改成员,因此不能将其存储为 lambda。剩下的是函数包装器和函数指针。您仍然可以为函数使用模板,但无论如何必须立即包装或转换 lambda,因此使用模板没有什么好处。

    【讨论】:

    • 我不明白 3 个枚举选项和倒数第二句话。似乎您可以有一个模板方法,将 lambda 作为F&amp;&amp;,但将参数存储在std::function 中。这些选项适用于哪个选项?
    • @MSalters I Reworder 为清晰起见。
    【解决方案3】:

    对于 C++20 及更高版本:

    如果您不打算存储传递的函数,您可以执行以下操作

    void use_it(auto func) {
        func();
    }
    

    您可以使用概念来进一步限制它,使其除了至少是可调用的以外什么都没有。

    如果您想存储参数,不幸的是您仍然需要像其他答案所建议的那样命名它,并且它必须能够转换为您指定的类型:

    #include <functional>
    
    class MyObject {
    public:
        void SetXGetter(auto func) {
            mLambda = std::move(func);
        }
    
    private:
        std::function<int()> mLambda;
    };
    

    【讨论】:

    • 我知道这个问题是关于 C++17 的,但我自己很好奇 C++20 如何改进这一点,并决定发布我的发现作为答案。对于编译器资源管理器上的代码:godbolt.org/z/r7qz13
    • (auto func) 的一个问题是传递一个仿函数左值可能会对原始参数类型产生不必要的副本。 use_it 可以只做auto&amp;&amp; func 来分别处理const lvalue 和其他情况,但忽略值类别。但是SetXGetter 可能应该使用完美转发,这对于auto 类型来说是困难的。或者坚持使用SetXGetter(std::function&lt;int()&gt;),它将任何“不可转换”错误更接近用户的上下文,并更好地自动处理重载/SFINAE。
    • @CJCombrink 非常有趣,感谢发帖
    猜你喜欢
    • 2012-12-27
    • 2018-09-09
    • 2015-11-11
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 2015-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多