Расширение пакета параметров в лямбда с выражением сгиба — gcc vs 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)(); }), ...);
}
};

лязг ++ 3.8+ успешно компилирует код.

g ++ 7.0 не компилируется со следующей ошибкой:

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 зов и лямбда заставляет g ++ компилировать выражение сгиба.

Является ли это взаимодействие между лямбдами, выражениями сворачивания и вызовами функций шаблона каким-то образом запрещенным стандартом, или это ошибка gcc?

6

Решение

Это старая ошибка gcc. Это один из немногих случаев, когда обработка шаблонов в 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)());
});
}
};

где мы тщательно избегаем расширения лямбды, пропуская лямбду каждый раз. Вместо этого мы берем набор аргументов и расширяем его до набора лямбда-вызовов.

Лямбда получает типы, переданные в виде тега, затем мы конвертируем его обратно в тип.

Живой пример

Не хранить тип возврата expand если вы прошли это временно.

9

Другие решения

Это хорошо известная ошибка в g ++ (# 47226), что было сообщено в … 2011.

4

По вопросам рекламы [email protected]