【问题标题】:Type of a lambda function, using autolambda 函数的类型,使用 auto
【发布时间】:2016-12-13 12:42:53
【问题描述】:

我正在尝试编写一个 c++ lambda 函数,并且不喜欢使用 auto 作为类型。目前它看起来像:

#include <iostream>

int main() {
//  Sends the address of an integer to a function which prints out the contents;
    auto print_int = [](int* a) {std::cout << *a << std::endl;};
    int a;
    a = 3;
    print_int(&a);
    return 0;
}

但是,我想将 auto 更改为 std::function&lt;void(int)&gt; 之类的东西,但不知道怎么做。

的答案

似乎相关,但我不知道如何适应它。谢谢。

【问题讨论】:

  • 你试过std::function&lt;void(int)&gt; = [](int * a) {std::cout &lt;&lt; *a &lt;&lt; std::endl;};吗?
  • 标准没有指定lambda函数的具体类型名称。当真正需要 lambda 时,您也不应该关心它。
  • auto 到底是什么问题?
  • 附带说明,如果您真的关心类型名称(我不明白您为什么会这样做),并且您的 lambda 是无捕获的,您可以将其分配给常规函数指针。
  • IMO,您应该只使用auto 并让编译器完成它的工作。即使有了多态性、模板、auto 关键字等,C++ 仍然是一种强类型语言,编译器仍然会确保无论 print_int 是什么,它都是类似于函数的东西,它愿意接受类似于指向 int 的指针的东西。不会有运行时意外。 --- 您使用此代码获得的一个运行时惊喜是当您尝试使用未初始化的*a = 3a 时遇到访问冲突/段错误。

标签: c++ c++11 lambda types


【解决方案1】:

Lambda 可以与auto 或模板参数一起使用。您永远不知道 lambda 的类型,也无法输入它。每个 lambda 都有自己独特的类型。即使你知道类型的名称,它们的类型名称通常也包含类型名称中禁止的字符。

为什么 lambda 有自己的类型?因为实际上,编译器会创建一个类似这样定义的类:

struct /* unnamed */ {

    // function body
    auto operator()(int* a) const {
        std::cout << *a << std::endl;
    }

} print_int; // <- instance name

此代码非常接近等效代码(我省略了转换运算符)。 如您所见,您已经使用了 auto,因为 lambdas 正在推断返回类型。

有些人会说使用std::function&lt;void(int*)&gt;,但我不同意。 std::function 是围绕任何可调用对象的多态包装器。由于 lambda 是可调用类型,因此它们适合它。换句话说,它的工作方式很像std::any,但需要一个呼叫操作员。它会在您的应用程序中产生开销。

那你该怎么办?

使用autoauto 还不错。事实上,它甚至可以使您的代码更快并减少不必要的输入。如果您对auto 感到不舒服,那么您不应该这样做! auto 很棒,尤其是在你别无选择的情况下;)

事实上,您可以通过使用模板参数来避免使用auto

template<typename F, typename Arg>
void parametric_print(F function, Arg&& arg) {
    function(std::forward<Arg>(arg));
}

然后像这样使用它:

int main() {
    int a = 3;
    parametric_print([](int* a) {std::cout << *a << std::endl;}, &a);
}

你去吧,不auto!但是,模板参数的推导规则与auto 相同。事实上,C++20 标准通过简洁的函数模板接受了概念。您可以像这样编写相同的函数模板:

// C++20
void parametric_print(auto function, auto&& arg) {
    function(std::forward<decltype(arg)>(arg));
}

正如 Oktalist 所提到的,如果概念被标准接受,那么您可以将 auto 替换为 Callable

Callable print_int = [](int* a) { std::cout << *a << std::endl; };

但它不会导致不同的类型,它只是在推断类型时强制执行一些规则。

【讨论】:

  • 如果概念被接受,你可以用 Callable print_int = [](int* a){...}; 做 OP 想要的事情 在此之前,auto 只是一个更宽松的版本。
【解决方案2】:

给你:

int a;
[](int* a) {std::cout << *a << std::endl;}(&a);

没有使用auto


但是,我想将自动更改为 std::function&lt;void(int)&gt; 之类的东西,但不知道如何。

这当然是可能的。 std::functional 有一个模板化的转换构造函数。您只需将 lambda 传递给构造函数,这就是它的全部内容。此外,您需要修复参数类型,因为您的 lambda 需要 int*,而不是 int(这是 auto 修复的那种错误自动)。

std::function<void(int*)> print_int = [](int* a) {std::cout << *a << std::endl;};

但请注意,这样做有两个缺点。

  • std::function 包装器是一个间接层,它隐藏了可调用对象的实际类型。此包装层增加了运行时开销并阻止了一些优化。
  • 如果更改返回值或 lambda 的参数,则必须手动更新类型。

【讨论】:

    【解决方案3】:

    lambda 有自己的未命名类型。

    您可以将您的 lambda 转换为 std::function&lt;void(int*)&gt;
    (或者对于 void (*)(int*) 的无捕获 lambda)。

    你可以显式地创建你的函子,然后给它一个名字,比如:

    class Print_int
    {
    public:
        void operator()(int* a) const {std::cout << *a << std::endl;}
    };
    

    然后

    Print_int print_int;
    

    【讨论】:

    • 希望避免上课。
    猜你喜欢
    • 2011-12-04
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多