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

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

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