Lambda捕获是否支持可变模板参数

8

如果我能做到以下几点,那就太好了:

template <class RT, class... PT>
class Event 
{
    ...
    void operator()(PT... args)
    {
        std::for_each(
            l.begin(), l.end(), [args...](Handler *p) { (*p)(args...); }
        );
    }
    ...
};

很遗憾,我无法使用g++ 4.7.2 (-std=c++0x)编译它:

evtempl.hh:在成员函数'void elt :: Event :: operator()(PT ...)'中: evtempl.hh:75:54: 错误:在“...”标记之前需要“,” evtempl.hh:75:54: 错误:在“...”标记之前需要标识符 evtempl.hh:75:57: 错误:未展开参数包: evtempl.hh:75:57: 注意:'args' evtempl.hh:在lambda函数中: evtempl.hh:76:26: 错误:扩展模式“args”不包含任何参数包 evtempl.hh:在实例化'void elt :: Event :: operator()(PT ...)[with RT = void; PT = {int}]'时: testevtempl.cc:28:9: 在此需要 evtempl.hh:74:9: 错误:使用无效字段'elt :: Event :: operator()(PT ...)::::Handler*)>> :: __args' evtempl.hh:在实例化'void elt :: Event :: operator()(PT ...)[with RT = void; PT = {int,const char *}]'时: testevtempl.cc:29:20: 在此需要 evtempl.hh:74:9: 错误:使用无效字段'elt :: Event :: operator()(PT ...)::::Handler*)>> :: __args'

相反,我必须将该lambda更改为旧的单调语法:

for (itr = l.begin(); itr != l.end(); ++itr)
     (*(*itr))(args...);

这个程序编译并且运行正常。

我想知道为什么lambda语法不起作用。

  1. 我做错了什么或者错过了什么?
  2. c++11标准禁止这种语法吗?
  3. 还是这种语法在标准中允许,但是当前编译器有问题?

我尝试过

[=](Handler *p) { (*p)(args...); }

如果您执行以下操作,它将给出与该错误相同的错误:

[args](Handler *p) { (*p)(args...); }

投诉参数包没有展开


3
你试过用 [=] 替代吗? - Kerrek SB
3
如果你把 lambda 表达式中的 auto foo = [=](Handler *p) { (*p)(args...); }; 部分拆开来看,你会得到两个错误提示:“1)参数包未展开”和“2)展开模式 'args' 不包含参数包”。我怀疑这是 GCC 的一个 bug,因为它既在抱怨 args 不是一个参数包,又在说 args 必须是一个参数包。 - Dave S
@lethal-guitar:那个问题很相似。然而,这个问题使用显式捕获,而链接的问题则是在讨论隐式捕获。 - Jesse Good
@JesseGood 啊是的,你说得对。 - lethal-guitar
它在 clang 上运行,http://liveworkspace.org/code/pstVG$3 - oblitum
显示剩余2条评论
1个回答

7

实际上,这个问题已经存在很长时间了,据我所知,在gcc 4.8和clang中仍然存在这个问题。 - gnzlbg
@gnzlbg,这不是clang中的错误。 - oblitum
@chico 很酷!谢谢!我不知道它已经在clang中工作了!如果只有将 f(args)...; 扩展为 f(arg1), f(arg2), ...; 就好了... - gnzlbg
@gnzlbg,有一个技巧可以仅为其副作用扩展包模式:std::initializer_list<int>{ (void(f(args)), 0)... };。您可以将其放入宏或其他内容中。请注意,{}初始化保证评估顺序。 - R. Martinho Fernandes
f(args), 0 执行 f(args),并且返回零(如果您不熟悉逗号运算符,请查阅)。我在 f 周围添加了 void 作为预防措施,以防万一出现重载的逗号运算符,我们不希望它在这里起作用(因为您不能使用 void 参数重载它)。所以我只是构建了一个初始化列表,其中包含所有这些副作用。初始化列表会立即被丢弃。应该没有额外开销。最坏的情况是,您的编译器会在可执行文件中存储一个只读静态数组,但我希望编译器能够意识到它没有被使用。 - R. Martinho Fernandes

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