【问题标题】:Expanding parameter pack into lambda with fold expression - gcc vs clang使用折叠表达式将参数包扩展为 lambda - gcc vs clang
【发布时间】:2016-11-22 22:10:44
【问题描述】:

考虑如下代码sn-p:

template <typename TF>
void post(TF){ }

template <typename... TFs>
struct funcs : TFs...
{
    funcs(TFs... fs) : TFs{fs}... { }

    void call() 
    { 
        (post([&]{ static_cast<TFs&>(*this)(); }), ...); 
    }
};

clang++ 3.8+ successfully compiles the code.

g++ 7.0 fails to compile 出现以下错误:

prog.cc: In lambda function:
prog.cc:10:43: error: parameter packs not expanded with '...':
        (post([&]{ static_cast<TFs&>(*this)(); }), ...);
                   ~~~~~~~~~~~~~~~~~~~~~~~~^~
prog.cc:10:43: note:         'TFs'
prog.cc: In member function 'void funcs<TFs>::call()':
prog.cc:10:13: error: operand of fold expression has no unexpanded parameter packs
        (post([&]{ static_cast<TFs&>(*this)(); }), ...);
         ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

删除 post 调用和 lambda makes g++ compile the fold expression

lambda、折叠表达式和模板函数调用之间的这种交互是否被标准以某种方式禁止,或者这是一个 gcc 错误?

【问题讨论】:

  • GCC 有 long-standing issues 与打包扩展整个 lambda。不完全是新的。
  • 我会选择 3 号门,Monty:尚未完全实施。

标签: c++ lambda language-lawyer c++17 fold-expression


【解决方案1】:

这是old gcc bug。这是 gcc 的模板处理比 MSVC 差的少数情况之一。可耻的 gcc。耻辱。

有时可行的解决方法是使用标签和包扩展。

template<class T>struct tag_t{using type=T; constexpr tag_t(){};};
template<class T>constexpr tag_t<T> tag{};
template<class Tag>using type_t=typename Tag::type;
#define TAG2TYPE(...) type_t<decltype(__VA_ARGS__)>

// takes args...
// returns a function object that takes a function object f
// and invokes f, each time passing it one of the args...
template<class...Args>
auto expand( Args&&...args ) {
  return [&](auto&& f)->decltype(auto) {
    using discard=int[];
    (void)discard{0,(void(
      f( std::forward<Args>(args) )
    ),0)...};
  };
}

template <typename TF>
void post(TF){ }

template <typename... TFs>
struct funcs : TFs...
{
  funcs(TFs... fs) : TFs{fs}... { }

  void call()  { 
    expand( tag<TFs>... )
    ([&](auto tag){
      post(static_cast< TAG2TYPE(tag)& >(*this)());
    });
  }
};

我们通过每次传入 lambda 来小心避免扩展 lambda 的末尾。相反,我们采用一组参数并将其扩展为一组 lambda 调用。

lambda 获取作为标签传入的类型,然后我们将其转换回类型。

Live example

如果您临时传递了expand,则不要存储返回类型。

【讨论】:

    【解决方案2】:

    这是一个众所周知的 g++ 错误 (#47226),于 2011 年报告。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-23
      • 1970-01-01
      • 2021-10-31
      • 2019-12-25
      • 2020-11-29
      • 1970-01-01
      • 2018-11-03
      相关资源
      最近更新 更多