为啥我不能在 C++11 中创建一个 lambda 向量(相同类型)?

Posted

技术标签:

【中文标题】为啥我不能在 C++11 中创建一个 lambda 向量(相同类型)?【英文标题】:Why can't I create a vector of lambdas (of the same type) in C++11?为什么我不能在 C++11 中创建一个 lambda 向量(相同类型)? 【发布时间】:2011-11-20 14:18:09 【问题描述】:

我试图创建一个 lambda 向量,但失败了:

auto ignore = [&]()  return 10; ;  //1
std::vector<decltype(ignore)> v;     //2
v.push_back([&]()  return 100; );  //3

直到第 2 行,它是compiles fine。但是第 3 行给出了compilation error:

错误:没有匹配函数调用 'std::vector

>::push_back(main()::)'

我不想要函数指针向量或函数对象向量。但是,封装 real lambda 表达式的函数对象向量对我有用。这可能吗?

【问题讨论】:

“我不想要函数指针向量或函数对象向量。”但这就是你要求的。 lambda 一个函数对象。 密切相关:What is the type of lambda when deduced with “auto” in C++11?. 【参考方案1】:

每个 lambda 都有不同的类型——即使它们具有相同的签名。如果您想做类似的事情,您必须使用运行时封装容器,例如 std::function

例如:

std::vector<std::function<int()>> functors;
functors.push_back([&]  return 100; );
functors.push_back([&]  return  10; );

【讨论】:

管理一个一百人的开发团队对我来说更像是一场噩梦 :) 另外,不要忘记无捕获 lambda([] 样式)可以降级为函数指针。所以他可以存储一个相同类型的函数指针数组。请注意,VC10 还没有实现。 顺便说一句,在这些示例中不应该使用 capture-less 吗?还是有必要? - 顺便说一句,VC11 似乎支持函数指针的无捕获 lambda。不过没有测试。 是否可以创建不同类型的向量存储函数?即我可以使用不同的函数原型,而不是将其限制为std::function&lt;int() @manatttta 有什么意义?容器的存在是为了存储相同类型的对象,将它们组织和操作在一起。你不妨问'我可以创建一个vector 存储std::functionstd::string 吗?答案是一样的:不,因为那不是预期用途。您可以使用“变体”风格的类来执行足够的类型擦除以将不同的东西放入容器中,同时包括一种方法供用户确定“真实”类型并因此选择要做什么(例如如何调用)每个元素......但同样,为什么要这么长?有什么真正的理由吗?【参考方案2】:

所有 lambda 表达式都有不同的类型,即使它们逐个字符都相同。您将不同类型的 lambda(因为它是另一个表达式)推入向量中,这显然行不通。

One solution 是改为制作std::function&lt;int()&gt; 的向量。

auto ignore = [&]()  return 10; ;
std::vector<std::function<int()>> v;
v.push_back(ignore);
v.push_back([&]()  return 100; );

另一方面,当您没有捕获任何内容时,使用 [&amp;] 并不是一个好主意。

【讨论】:

对于不带参数的 lambda,不需要 ()【参考方案3】:

虽然其他人说的很相关,但仍然可以声明和使用 lambda 向量,尽管它不是很有用:

auto lambda = []  return 10; ;
std::vector<decltype(lambda)> vec;
vec.push_back(lambda);

因此,您可以在其中存储任意数量的 lambda,只要它是 lambda 的副本/移动!

【讨论】:

如果推回发生在具有不同参数的循环中,这实际上可能很有用。大概是出于惰性评估的目的。 不,你没有把参数放在向量中,只是函数对象。所以它是一个向量,所有副本都具有相同的 lambda【参考方案4】:

如果您的 lambda 是无状态的,即[](...)...,C++11 允许它降级为函数指针。理论上,兼容 C++11 的编译器可以编译这个:

auto ignore = []()  return 10; ;  //1 note misssing & in []!
std::vector<int (*)()> v;     //2
v.push_back([]()  return 100; );  //3

【讨论】:

为了记录,auto ignore = *[] return 10; ; 将使ignore 成为int(*)() @Luc,哦,太恶心了!他们什么时候添加的? 好吧,因为首先允许使用函数指针的转换函数被强制要求不是explicit,所以取消引用 lambda 表达式是有效的,并且取消引用转换产生的指针。然后使用auto 将该引用衰减回指针。 (使用 auto&amp;auto&amp;&amp; 会保留引用。) 啊...取消引用结果指针。这就说得通了。丢失() 是有意还是无意? 有意的,lambda 表达式是等价的(但短了两个字符)。【参考方案5】:

您可以使用 lambda 生成函数(使用 Nawaz 建议的修复更新):

#include <vector>
#include <iostream>

int main() 
    auto lambda_gen = [] (int i) return [i](int x) return i*x;; ;

    using my_lambda = decltype(lambda_gen(1));

    std::vector<my_lambda> vec;

    for(int i = 0; i < 10; i++) vec.push_back(lambda_gen(i));

    int i = 0;

    for (auto& lambda : vec)
        std::cout << lambda(i) << std::endl;
        i++;
    

但我认为此时您基本上已经创建了自己的课程。否则,如果 lambda 具有完全不同的 caputres/args 等,您可能必须使用元组。

【讨论】:

将它包装在像 lambda_gen 这样的函数中的好主意,它本身又可以是一个 lambda。但是,auto a = lambda_gen(1); 进行了不必要的调用,如果我们编写此 decltype(lambda_gen(1)),则可以避免这种情况。 那不是还要打额外电话吗?另一个小问题是问题说明了 C++11,所以我认为需要在函数中添加尾随返回类型。 没有。 decltype 中的任何内容都未评估,因此实际上并未进行调用。 sizeof 也是如此。此外,即使您添加尾随返回类型,此代码也无法在 C++11 中运行!!【参考方案6】:

每个 lambda 都是不同的类型。您必须使用std::tuple 而不是std::vector

【讨论】:

以上是关于为啥我不能在 C++11 中创建一个 lambda 向量(相同类型)?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们不能在c中创建一个带有常量的数组[重复]

为啥我不能在 Ubuntu 中创建原始套接字?

为啥我不能在主类中创建除主方法之外的另一个方法?

为啥我不能在 cx_Freeze 中创建线程池?

为啥在 lambda 表达式中使用迭代变量不好

为啥我不能在 SQL 中创建游标?