【问题标题】:parameter pack expansion not working in lambda参数包扩展在 lambda 中不起作用
【发布时间】:2017-09-09 19:17:46
【问题描述】:

我正在通过做一些练习来学习可变参数模板,但当涉及到 lambda 中的参数包扩展时,我被卡住了

所以,我的想法是编写一个计时器类,其有效负载将是可调用的,但是当我尝试在 lambda 函数中展开参数包时出现编译错误..

gcc 版本 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3)

   template<typename F, typename... Args>
   struct timer
   {   
      timer(const std::chrono::milliseconds milliseconds, F call, Args&&... args)
      {   
         m_timer = std::make_shared<boost::asio::steady_timer>(
               timer_manager::instance().get_io_service(),
               std::chrono::steady_clock::now() + milliseconds
         );  

         //m_timer->async_wait(call, std::forward<Args>(args)...);
         m_timer->async_wait([=](const boost::system::error_code& ec){
             call(std::forward<Args>(args)...); //Error here
         }); 
      }   

      std::shared_ptr<boost::asio::steady_timer> m_timer;
   };


   // Helper to create the timer
   template<typename F, typename... Args>
   timer<F,Args...> create_timer(const std::chrono::milliseconds milliseconds,F callable, Args&& ...args)
   {   
      return timer<F,Args...>(milliseconds, std::forward<F>(callable), std::forward<Args>(args)...);
   }   

使用它的主程序:

auto timer = timer::create_timer(std::chrono::milliseconds(5000), []()                                                                                                                  
{
   std::cout << "timer fired in main" << std::endl;
   payload::execute(10);           
 });

错误:

home/samdaniel/timer/src/timer.hpp: In lambda function:
/home/samdaniel/timer/src/timer.hpp:36:43: error: parameter packs not expanded with ‘...’:
                   call(std::forward<Args>(args)...);
                                           ^
/home/samdaniel/timer/src/timer.hpp:36:43: note:         ‘args’
/home/samdaniel/timer/src/timer.hpp: In instantiation of ‘struct timer::timer<F, Args>::timer(std::chrono::milliseconds, F, Args&& ...) [with F = main()::__lambda1; Args = {}; std::chrono::milliseconds = std::chrono::duration<long int, std::ratio<1l, 1000l> >]::__lambda0’:
/home/samdaniel/timer/src/timer.hpp:35:10:   required from ‘timer::timer<F, Args>::timer(std::chrono::milliseconds, F, Args&& ...) [with F = main()::__lambda1; Args = {}; std::chrono::milliseconds = std::chrono::duration<long int, std::ratio<1l, 1000l> >]’
/home/samdaniel/timer/src/timer.hpp:50:99:   required from ‘timer::timer<F, Args ...> timer::create_timer(std::chrono::milliseconds, F, Args&& ...) [with F = main()::__lambda1; Args = {}; std::chrono::milliseconds = std::chrono::duration<long int, std::ratio<1l, 1000l> >]’
/home/samdaniel/src/main.cpp:21:11:   required from here
/home/samdaniel/timer/src/timer.hpp:36:43: error: using invalid field ‘timer::timer<F, Args>::timer(std::chrono::milliseconds, F, Args&& ...)::__lambda0::__args’
make[2]: *** [src/CMakeFiles/coding_with_me.dir/main.cpp.o] Error 1
make[1]: *** [src/CMakeFiles/coding_with_me.dir/all] Error 2
make: *** [all] Error 2

新示例:

#include <iostream>
#include <functional>

namespace test
{
   template<typename F, typename... Args>
   void callback_dispatcher(F call, Args&& ...args )
   {   
      std::cout << "callback_dispatcher>" << __PRETTY_FUNCTION__ << "enter <<< " << std::endl;                                                                                                 
      auto invoke_me = [=](){
         call(std::forward<Args>(args)...);
      };  
      invoke_me();
   }   
}

int main()
{
   std::cout << "Main entered..." << std::endl;


   test::callback_dispatcher([](int a)
         {
         std::cout << "void(int) lambda dispatched with a = " << a << std::endl;
         },5);

   std::cout << "Main exited..." << std::endl;
}

错误:

src/generic_callback.cc: In lambda function:
src/generic_callback.cc:11:34: error: parameter packs not expanded with ‘...’:
          call(std::forward<Args>(args)...);
                                  ^
src/generic_callback.cc:11:34: note:         ‘args’
src/generic_callback.cc: In instantiation of ‘struct test::callback_dispatcher(F, Args&& ...) [with F = std::_Bind<std::_Mem_fn<void (plo_callback_tester::*)()>(std::_Placeholder<1>)>; Args = {plo_callback_tester* const}]::__lambda0’:
src/generic_callback.cc:12:7:   required from ‘void test::callback_dispatcher(F, Args&& ...) [with F = std::_Bind<std::_Mem_fn<void (plo_callback_tester::*)()>(std::_Placeholder<1>)>; Args = {plo_callback_tester* const}]’
src/generic_callback.cc:25:101:   required from here
src/generic_callback.cc:11:34: error: using invalid field ‘test::callback_dispatcher(F, Args&& ...)::__lambda0::__args’
src/generic_callback.cc: In instantiation of ‘struct test::callback_dispatcher(F, Args&& ...) [with F = main()::__lambda1; Args = {int}]::__lambda0’:
src/generic_callback.cc:12:7:   required from ‘void test::callback_dispatcher(F, Args&& ...) [with F = main()::__lambda1; Args = {int}]’
src/generic_callback.cc:41:13:   required from here
src/generic_callback.cc:11:34: error: using invalid field ‘test::callback_dispatcher(F, Args&& ...)::__lambda0::__args’

【问题讨论】:

  • 我不确定(这就是为什么这是评论而不是答案),但我认为你想做std::forward&lt;Args...&gt;(args...);即,将... 移动到括号和模板参数列表中。我认为这里也不需要模板参数,但我又不确定。
  • @DanielH: 不,如果Args&amp;&amp;... 是转发参考包,std::forward&lt;Args&gt;(args)... 将是正确的。在构造函数中 std::move(args)... 就足够了。
  • coliru.stacked-crooked.com/a/b291d1dff8829497 -- 在消除不应该改变错误的东西之后,这些编译器中的任何一个都没有错误。请提供一个实际的minimal reproducible example,它既能重现问题,又能消除所有那些额外不重要的依赖关系。除非你认为timer_manager 和所有的 asio 东西实际上会导致参数包不扩展错误,我不相信。诚然,这些是编译器的更高版本,但我会让在你的编译器中生成minimal reproducible example,而不是试图读懂你的想法。
  • @Yakk,向用作回调的 lambda 添加一个参数,然后事情就中断了 - coliru.stacked-crooked.com/a/15c0bf4fc22316e6 ,但我不确定在捕获期间参数包的类型会发生什么情况在 lambda 旁边以及它为什么会中断:(
  • 但是,是的,删除 lambda 中似乎不需要的 std::forward 可以解决参数问题。忽略我之前的评论。

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


【解决方案1】:
#include <iostream>

namespace test
{
  template<typename F, typename... Args>
  void callback_dispatcher(F call, Args&& ...args )
  {   
    std::cout << "callback_dispatcher>" << __PRETTY_FUNCTION__ << "enter <<< " << std::endl;                                                 
    auto invoke_me = [=]()mutable{
      call(std::move(args)...);
    };  
    invoke_me();
  }   
}

int main()
{
  std::cout << "Main entered..." << std::endl;


  test::callback_dispatcher(
    [](int a)
    {
      std::cout << "void(int) lambda dispatched with a = " << a << std::endl;
    },
    5
  );

  std::cout << "Main exited..." << std::endl;
}

上述细微变化在 gcc 4.9 及更高版本中编译,但在 gcc 4.8.5 中不编译。

它也可以在现代版本的 clang 中编译。

更新您的编译器,或者不要使用您的编译器不支持的 c++11 功能。

您可以将 args 打包成一个元组,然后将它们解包到函数体中。

  template<typename F, typename... Args>
  void callback_dispatcher(F call, Args&& ...args )
  {   
    std::cout << "callback_dispatcher>" << __PRETTY_FUNCTION__ << "enter <<< " << std::endl;                                                 
    auto invoke_me = [tup = std::make_tuple(std::forward<Args>(args)...), call=std::move(call)]()mutable{
      std::apply( call, std::move(tup) );
    };  
    invoke_me();
  }   

您现在必须在 C++11 的有限子集中实现 std::apply,这是 C++17 的一项功能。 (不要称它为std::apply)。这种方法可能适用于 g++ 4.8。

namespace notstd {
  template<std::size_t...Is> struct index_sequence {using type=index_sequence;};
  template<std::size_t N, std::size_t...Is> struct make_index_sequence:make_index_sequence<N-1, N-1, Is...>{};
  template<std::size_t...Is> struct make_index_sequence<0,Is...>:index_sequence<Is...>{};
  namespace details {
    template<class F, class Tuple, std::size_t...Is>
    auto apply( ::notstd::index_sequence<Is...>, F&& f, Tuple&& tuple )
    -> decltype( std::declval<F>()( std::get<Is>(std::declval<Tuple>())... ) )
    {
      return std::forward<F>(f)( std::get<Is>(std::forward<Tuple>(tuple))... );
    }
  }
  template<class F, class Tuple>
  auto apply( F&& f, Tuple&& tuple )
  -> decltype(::notstd::details::apply( make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{}, std::forward<F>(f), std::forward<Tuple>(tuple) ))
  {
    return ::notstd::details::apply( make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{}, std::forward<F>(f), std::forward<Tuple>(tuple) );
  }
}

live example of C++11 compliant notstd::apply.

【讨论】:

    【解决方案2】:

    如果扩展在 lambda 中不起作用,则可以在创建功能对象期间将包扩展到外部并将其传递给该 lambda。

    所以而不是:

    m_timer->async_wait([=](const boost::system::error_code& ec){
        call(std::forward<Args>(args)...); //Error here
    }); 
    

    可能是:

    auto functor = std::bind(std::forward<F>(call), std::forward<Args>(args)...);
    m_timer->async_wait([=](const boost::system::error_code& ec){
        functor();
    }); 
    

    【讨论】:

      【解决方案3】:

      我在使用 c++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36) 和 c++11 时也遇到同样的错误

      template<typename... Args>
          void emplace_front(Args&&... args)
          {
              target( [&] {
                           _collection.emplace_front(std::forward<Args>(args)...);
                           } );
          }
      

      以下对我有用!

      template<typename... Args>
          void emplace_front(Args&&... args)
          {
              target( [&] (Args ...ar) -> void {
                                 _collection.emplace_front(ar...);
                                 } );
          }
      

      我收到关于 Args&&... args 未使用但易于修复的警告。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-06-11
        • 2023-03-10
        • 2017-01-18
        • 2023-03-29
        • 2014-08-09
        • 2020-11-15
        • 2016-09-29
        相关资源
        最近更新 更多