【问题标题】:How do I use variadic perfect forwarding into a lambda?如何使用可变参数完美转发到 lambda?
【发布时间】:2012-12-20 22:45:52
【问题描述】:

我有一个调用 lambda 的工作函数模板。

我想将此函数模板概括为采用可变参数并将它们完美地转发到 lambda 中,但我无法编译此代码。

我使用的是 gcc 4.7.2。

更新

根据 R. Martinho Fernandes 的建议,我在 bugzilla -it does look like a bug that's been around for a while 中查找了错误。如果有人知道解决方法(我现在正在寻找一个),请发布答案 - ty。

错误

junk.cpp: In lambda function:
junk.cpp:32:68: error: parameter packs not expanded with ‘...’:
junk.cpp:32:68: note:         ‘args’
junk.cpp: In instantiation of ‘std::pair<std::basic_string<char>, typename T::Lambda> MP(const string&, M, Args&& ...) [with T = Integer; M = int (Integer::*)()const; Args = {}; typename T::Lambda = std::function<std::function<int()>(const Integer&)>; std::string = std::basic_string<char>]’:
junk.cpp:47:42:   required from here
junk.cpp:34:2: error: using invalid field ‘MP(const string&, M, Args&& ...)::<lambda(const T&)>::__args’
make: *** [junk] Error 1

代码

#include <functional>
#include <iostream>
#include <map>

struct Integer
{
    typedef std::function<int()>                            Function;
    typedef std::function<Function( Integer const& inst )>  Lambda;

    virtual int getInt() const = 0;
};

struct IntImpl : public Integer
{
    virtual int getInt() const { return 42; }
};

typedef std::function<int()>                               IntFunction;
typedef std::function<IntFunction( Integer const& inst )>  IntLambda;

#define WONT_COMPILE

template<typename T,typename M,typename... Args>
std::pair<std::string,typename T::Lambda>
MP( std::string const& str, M method, Args&&... args )
{
#ifdef WONT_COMPILE 
    return std::make_pair( str, 
        [=]( T const& inst ) 
        {
            // COMPILE ERROR (Line 32) on next line
            return std::bind( method, std::cref( inst ), std::forward<Args>(args)...);
        } 
    );
#else
    return std::make_pair( str, 
        [method]( T const& inst ) 
        {
            return std::bind( method, std::cref( inst ));
        } 
    );
#endif
}

std::map<std::string,IntLambda> const g_intTbl =
{
    MP<Integer>( "getInt", &Integer::getInt )
};

int
main( int argv, char* argc[] )
{
    IntImpl x;
    std::cerr << g_intTbl.find("getInt")->second( x )() << std::endl;
}

【问题讨论】:

  • 出现其中一些错误是因为 std::forward&lt;Args&gt;(args...) 应该是 std::forward&lt;Args&gt;(args)...
  • @AndreiTita +1 ty - 这有帮助 - 我仍然遇到错误 - OP 已更新
  • 好吧,我不知道您是否可以在 lambda 捕获中扩展可变参数包(稍后会检查),但解决方法是使用像 [=] 这样的完整捕获。
  • @AndreiTita @R.MartinhoFernandes 嗯... ...当我用[=] 替换捕获时,它肯定有所改进,但我仍然收到错误(也许是 gcc?) - 我会更新操作
  • 似乎是一个 gcc 错误。

标签: c++ binding c++11 lambda variadic-templates


【解决方案1】:

这似乎是编译器的错误(如果还没有,请报告)。标准说:

包展开由一个模式和一个省略号组成, 其实例化产生零个或多个实例化 列表中的模式(如下所述)。图案的形式取决于 在扩展发生的上下文中。包扩展可以 发生在以下情况:

— [...]
— 在 捕获列表 (5.1.2) 中;该模式是一种捕获。
— [...]

这使您的代码正确。

在您获得可以处理此问题的编译器之前,您将使用[=] 捕获所有内容作为解决方法。

【讨论】:

  • 嘿,我正要写同样的(但你有更好的标准引号)。具体来说,这是一个 gcc 错误 - MSVC(11 月 CTP)和 Clang 3.2 接受 lambda 捕获中的参数包扩展。
  • +1 用于解决问题:如果有人知道 gcc 解决方法,请发布 - ty
【解决方案2】:

如果有人知道解决方法(我现在正在寻找解决方法),请发布答案

我遇到了完全相同的问题并找到了解决方法。这是一个迟到的答案,希望你同时找到一个解决方案,但无论如何它都在这里(它至少对其他人有用)。

这个想法是改变 lambda 的参数,使其也接受与外部函数相同的可变参数(例如,[](int) {} 变为 [](int, Args&amp;&amp;... args) {})和bind lambda 到外部函数的可变参数。完成此操作后,在 lambda 中转发可变参数就没有问题了。

总结一下:

template<typename... Args>
std::function<void (int)> foo(Args&&... args) {
    return [&](int bar) {
                  // COMPILER BUG: doesn't work with GCC 4.7 despite the capture
                  doSomething(bar, std::forward<Args>(args)...);
              };
}

template<typename... Args>
std::function<void (int)> foo(Args&&... args) {
    return std::bind([](int bar, Args&&... args) {
                            // now this works with GCC 4.7
                            doSomething(bar, std::forward<Args>(args)...);
                       },
                     std::placeholders::_1, std::forward<Args>(args)...);
}

当然这是一个丑陋的 hack,但至少即使你遇到了一个有问题的编译器,你仍然可以获得预期的功能。

【讨论】:

  • 这是否按所写的那样工作?我认为您需要更改返回值的签名以考虑到 std::function 将 intArgs&amp;&amp;... 作为参数这一事实。
  • @Sean 不,我发布的代码按原样工作。无需更改foo 的返回值类型,因为Args&amp;&amp;... 部分在返回函子之前已绑定。这就是bind 实际上的全部意义:绑定函子参数的部分应用。
  • 是的,我对 bind 的实际用途有一个重大误解,感谢您解决这个问题。
  • [&amp;] 的第一个示例不会被完美转发,因为它将是参考捕获,而不是完美转发捕获?
猜你喜欢
  • 1970-01-01
  • 2013-01-06
  • 1970-01-01
  • 1970-01-01
  • 2012-11-28
  • 2015-10-03
  • 2011-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多