Выражение содержит нерасширенные пакеты параметров
Как-то я не понимаю, как расширяются пакеты параметров вариационного шаблона. Что не так с этим кодом?
#include <iostream>
template <typename T>
struct print_one
{
static void run(const T& t)
{
std::cout << t << ' ';
}
};
template<typename... Args>
void print_all(Args&&... args)
{
// the next line doesn't compile:
print_one<Args>::run(std::forward<Args>(args))...;
}
int main()
{
print_all(1.23, "foo");
}
Кланг говорит: Expression contains unexpanded parameter packs 'Args' and 'args'
. Почему?
Ответы
Ответ 1
...
должен войти в круглые скобки вызова функции:
print_one<Args>::run(std::forward<Args>(args)...);
Очевидно, что это не будет работать для вашей функции, которая принимает только один аргумент, поэтому вам нужно найти способ расширения вызовов в вызове функции или другой разрешенной конструкции:
// constructing a dummy array via uniform initialization
// the extra 0 at the start is to make it work when the pack is empty
int dummy[]{0, (print_one<Args>::run(std::forward<Args>(args)), 0)...};
// or, if your compiler doesn't support uniform initialization
int dummy[] = {0, (print_one<Args>::run(std::forward<Args>(args)), 0)...};
// or, calling a dummy function
template<typename... Args> void dummy(Args...) {}
dummy((print_one<Args>::run(std::forward<Args>(args)), 0)...);
// or, constructing a temporary dummy object
struct dummy { dummy(std::initializer_list<int>) {} };
dummy{(print_one<Args>::run(std::forward<Args>(args)), 0)...};
// or, constructing a temporary initializer list
std::initializer_list<int>{(print_one<Args>::run(std::forward<Args>(args)), 0)...};
Обратите внимание на использование оператора запятой, чтобы вернуть void
return print_one
в значение, подходящее для размещения в списке аргументов или выражении инициализации.
Формы списка инициализаторов предпочтительнее форм вызова функций, так как они (предположительно) являются заказанными LTR, которые не являются аргументами вызова функции.
Формы, в которых может произойти расширение пакета параметров, покрываются 14.5.3 [temp.variadic]:
4 - [...] Расширения пакетов могут возникать в следующих контекстах:
Ваш исходный код является незаконным, поскольку, хотя в текстовом выражении может показаться, что он должен выпустить оператор, состоящий из нескольких выражений с запятыми, которые не являются контекстом, разрешенным 14.5.3: 4.
Ответ 2
Стандарт определяет, где разрешено расширение пакета:
§14.5.3 [temp.variadic] p4
[...] Расширения пакетов могут возникать в следующих контекстах:
- В пакете параметров функций (8.3.5); шаблон представляет собой объявление параметра без эллипса.
- В пакете параметров шаблона, который является расширением пакета (14.1):
- если пакет параметров шаблона является декларацией параметра; шаблон представляет собой объявление параметра без эллипса;
- если пакет параметров шаблона является параметром типа с параметром-параметром-списком; шаблон является соответствующим параметром типа без эллипса.
- В списке инициализаторов (8.5); шаблон является предложением инициализатора.
- В списке базового спецификатора (п. 10); шаблон является базовым спецификатором.
- В списке mem-initializer (12.6.2); шаблон является mem-инициализатором.
- В списке шаблонов-аргументов (14.3); шаблон является аргументом шаблона.
- В спецификации динамического исключения (15.4); шаблон является идентификатором типа.
- В списке атрибутов (7.6.1); шаблон является атрибутом.
- В спецификаторе выравнивания (7.6.2); шаблон является спецификатором выравнивания без эллипса.
- В списке захвата (5.1.2); шаблон является захватом.
- В выражении
sizeof...
(5.3.3); шаблон является идентификатором.
Так что в основном, как оператор верхнего уровня, расширение не допускается. Обоснование этого? Без понятия. Скорее всего, они выбрали контексты, в которых разделяющая запятая (,
) является частью грамматики; в любом другом месте вы можете выбрать перегруженный operator,
, если задействованы определяемые пользователем типы и возникают проблемы.