使用折叠表达式将参数包扩展为lambda-GCC与Clang [英] Expanding parameter pack into lambda with fold expression - gcc vs clang

查看:129
本文介绍了使用折叠表达式将参数包扩展为lambda-GCC与Clang的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码段:

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 + 成功地编译了代码.

clang++ 3.8+ successfully compiles the code.

g ++ 7.0 无法编译,并出现以下错误:

g++ 7.0 fails to compile with the following error:

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 使g ++编译折叠表达式.

Removing the post call and the lambda makes g++ compile the fold expression.

lambda,折叠表达式和模板函数之间的交互是标准所禁止的,还是gcc错误?

推荐答案

这是旧的gcc错误.这是gcc的模板处理效果比MSVC差的少数情况之一.可耻的gcc.丢脸.

This is an old gcc bug. It is one of the few cases where gcc's template handling is worse than MSVC. Shame gcc. Shame.

一种有时可行的解决方法是使用标签和包装扩展.

A workaround that sometimes works is to use tags and pack expansion.

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调用.

where we carefully avoid expanding over the end of a lambda by passing the lambda in each time. Instead, we take a set of arguments and expand it into a set of lambda calls.

lambda获取作为标记传递的类型,然后将其转换回类型.

The lambda gets the types passed in as a tag, then we convert it back to a type.

在线示例

如果您临时将expand的返回类型保存下来,则不要存储.

Do not store return type of expand if you passed it temporaries.

这篇关于使用折叠表达式将参数包扩展为lambda-GCC与Clang的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆