Почему-то я не понимаю, как расширяются пакеты параметров шаблонов. Что не так с этим кодом?
#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");
}
Clang говорит, Expression contains unexpanded parameter packs 'Args' and 'args'
, Зачем?
...
должен идти внутри скобок вызова функции:
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
Возвращение print_one
в значение, подходящее для размещения в списке аргументов или выражении инициализатора.
Формы списка инициализатора предпочтительнее форм вызова функций, так как они (как предполагается) упорядочены LTR, а не аргументы вызова функции.
Формы, в которых может происходить расширение пакета параметров, описаны в 14.5.3 [temp.variadic]:
4 — […] Расширения пакета могут происходить в следующих контекстах:
- […]
Ваш исходный код недопустим, потому что, хотя в тексте может показаться, что он должен создавать оператор, состоящий из нескольких выражений с запятыми-операторами, это не является контекстом, разрешенным в 14.5.3: 4.
Стандарт определяет, где допускается расширение пакета:
§14.5.3 [temp.variadic] p4
[…] Расширения пакета могут происходить в следующих контекстах:
- В пакете параметров функции (8.3.5); шаблон является Параметр декларирование без многоточия.
- В пакете параметров шаблона, который является расширением пакета (14.1):
- если пакет параметров шаблона является Параметр декларирование; шаблон является Параметр декларирование без многоточия;
- если пакет параметров шаблона является типа параметра с шаблон-список параметров; шаблон соответствует типа параметра без многоточия.
- В инициализатора-лист (8.5); шаблон является инициализатор-раздел.
- В базовый спецификатор-лист (Пункт 10); шаблон является базовый спецификатор.
- В мем-инициализатора-лист (12.6.2); шаблон является мем-инициализатор.
- В Шаблон-аргумент-список (14.3); шаблон является Шаблон-аргумент.
- В Исключение динамически-спецификация (15,4); шаблон является тип-идентификатор.
- В список атрибутов (7.6.1); шаблон является атрибут.
- В Выравнивание спецификатор (7.6.2); шаблон является Выравнивание спецификатор без многоточия.
- В Захват-лист (5.1.2); шаблон является захватить.
- В
sizeof...
выражение (5.3.3); шаблон является идентификатором.
Таким образом, в основном, как утверждение верхнего уровня, расширение не допускается. Обоснование этого? Без понятия. Скорее всего, они только выбрали контексты, где разделяющая запятая (,
) является частью грамматики; где-нибудь еще вы можете выбрать перегруженный operator,
если пользовательские типы участвуют и попадают в неприятности.