使用本地Lambda时,模板函数会导致编译器错误。

5

我的之前的问题得出结论,使用C++ lambda函数(即函数对象)可能需要令人不悦的“双重转换”才能使用POSIX makecontext。现在,我面临一个编译错误,与以下最小代码相关:

#include <iostream>
#include <ucontext.h>

using namespace std;

template <typename T> void foo()   {
  ucontext_t c;
  auto f = [=](int i){ cout << i << endl; };
  makecontext(&c, (void (*) (void)) (void (*)(int)) f, 1, 12345);
}

int main(int argc, char *argv[]) {
  foo<int>();
  return 0;
}

错误信息为:

error: invalid cast from type ‘foo() [with T = int]::<lambda(int)>to type ‘void (*)(int)’

然而,如果我从foo函数中删除未使用的(在此示例中)模板参数,使其变为void foo();,并将调用更改为foo(),则错误消失了。有人能告诉我为什么吗?我正在使用G++ 4.6。

编辑:

从下面的评论中看来,代码中的[=]导致lambda成为一个“捕获”lambda,而实际上它并没有捕获任何东西。在我的代码中不需要[=],但遗憾的是,在GCC 4.6中用[]替换并不能消除错误。我现在正在安装GCC 4.6.1...

1
这在GCC 4.6.1中编译得很好。 - Kerrek SB
注意,这并不是预期的行为。正如你在答案中看到的,捕获lambda表达式不能转换为函数指针。这可能是一种优化,因为你没有捕获任何东西,但如果你将c添加到lambda体中,它将失败。 - Kerrek SB
嗯,它可以在4.6.1上编译,但如果我将其变为捕获lambda,那也会编译:( - user2023370
你必须在lambda函数体中引用并实际使用捕获,否则它可能会被优化掉。 - Kerrek SB
是的,我做了那个。我添加了一个新的 intj,以便输出 cout << i << " " << j << endl; - user2023370
2个回答

5
如果使用 [=] 去调用lambda函数,你将无法获取一个函数指针(或者可以转换为函数指针的对象),而是会得到一个函数对象。任何类型的强制类型转换都无法将其传递给 makecontext 。没有任何一种方式可以实际工作。
根据C++0x的最新工作草案N3291:
闭包类型对于没有 lambda 捕获的 lambda 表达式具有公共的非虚拟非显式 const 转换函数,该转换函数返回与闭包类型的函数调用运算符具有相同参数和返回类型的函数指针。 通过这个转换函数返回的值应该是一个函数的地址,当被调用时,具有与调用闭包类型的函数调用运算符相同的效果。
这是规范允许转换为函数指针的唯一地方。因此,如果近期版本的GCC确实允许 [=] 转换为函数指针,则不符合规范。

3
实际上,[=][&]只应捕获所使用的内容。由于来自外部范围的任何内容都没有被使用,因此它应该像[]一样运作。 - GManNickG
@Nicol:无捕获lambda产生的函数对象可以转换为函数指针。请查看我的先前问题链接。 - user2023370
@GMan:看看我的编辑。它绝对不应该像[]一样行事。[=]具有默认捕获,根据语法规则算作捕获。 - Nicol Bolas
我知道无需捕获的lambda可以转换为函数指针。但是[=]不是一个无需捕获的lambda,这就是我回答的重点。 - Nicol Bolas
@Nicol:嗯,非常有趣。我确信他们打算将任何没有捕获内容的lambda转换为函数指针,但你是正确的:根据语法,它不能这样。我试图寻找一份报告,看看是否有人注意到了这一点,但没有找到任何东西。就像你的答案所说,如果确实不需要任何捕获,我们可以将其改为[],但是为了一致性,他们应该用不同的措辞来表达。那么+1。我必须看看为什么gcc允许它,如果我发现了什么,我会回来告诉你。 - GManNickG
@Nicol:谢谢你,非常有趣。我很乐意在这里改为[],但不幸的是它并没有消除错误。这可能是Kerrek在GCC 4.6.1中没有看到的错误,我现在正在安装... - user2023370

3
只有没有捕获的lambda表达式可以转换为函数指针;虽然f在技术上没有捕获任何东西,但它确实具有默认的按值捕获模式(出于无法解释的原因)。
f的声明中将[=]更改为[],就可以按预期工作了。 编辑:这个事实在较新版本的GCC中编译通过(正如Kerrek所指出的),这强烈表明这只是您正在使用的版本中的编译器错误。

我尝试将其改为[],但对错误没有任何影响。听到关于GCC 4.6.1的这个消息很有趣。 - user2023370
我假设这个lambda表达式实际上没有捕捉任何东西,因为[=]只是在编译时解析的语法糖,在这种情况下解析为[]。(请参见下面GMan的评论。) - Kerrek SB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接