Выражение содержит нерасширенные пакеты параметров

Почему-то я не понимаю, как расширяются пакеты параметров шаблонов. Что не так с этим кодом?

#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', Зачем?

13

Решение

... должен идти внутри скобок вызова функции:

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.

24

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

Стандарт определяет, где допускается расширение пакета:

§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, если пользовательские типы участвуют и попадают в неприятности.

4

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