Extension of parameter package in lambda with fold expression - gcc vs clang

Given the following code snippet:

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 could not 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)(); }), ...); ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

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

Is this the interaction between lambdas, fold expressions and template calls, somehow forbidden by the standard, or is it a gcc bug?

+6
source share
2 answers

This is an old gcc bug . This is one of the few cases where handling gcc templates is worse than MSVC. Shame on gcc. Shame.

A workaround that sometimes works is to use tags and expand the package.

 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)()); }); } }; 

where we carefully avoid expansion at the end of the lambda, passing through each lambda. Instead, we take a set of arguments and expand it into a set of lambda calls.

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

Living example

Do not save the return type expand if you passed temporary files to it.

+9
source

This is a well-known g ++ bug ( # 47226 ), which was reported in ... 2011.

+4
source

Source: https://habr.com/ru/post/1012571/


All Articles