lambda函数的类型,使用auto

时间:2016-12-13 12:42:53

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

我正在尝试编写一个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<void(int)>,但不确定如何。

的答案

似乎相关,但我不确定如何适应它。感谢。

3 个答案:

答案 0 :(得分:7)

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<void(int*)>,但我不同意。 std::function是围绕任何可调用的多态包装器。由于lambdas是可调用类型,因此它们适合它。换句话说,它的工作方式与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; };

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

答案 1 :(得分:2)

你走了:

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

未使用auto

  

但是,我想将自动更改为std::function<void(int)>,但不确定如何。

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

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

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

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

答案 2 :(得分:1)

lambda有自己的未命名类型。

您可以将您的lambda转换为std::function<void(int*)>
(或无捕捉的lambda到void (*)(int*))。

您可以明确地创建您的仿函数,以便为其命名,例如:

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

然后

Print_int print_int;