Расширение пакета вариационных шаблонов
Я пытаюсь изучить вариативные шаблоны и функции. Я не могу понять, почему этот код не компилируется:
template<typename T>
static void bar(T t) {}
template<typename... Args>
static void foo2(Args... args)
{
(bar(args)...);
}
int main()
{
foo2(1, 2, 3, "3");
return 0;
}
когда я компилирую его не удается с ошибкой:
ошибка C3520: 'args': пакет параметров должен быть расширен в этом контексте
(в функции foo2).
5 ответов:
одно из мест, где может произойти расширение пакета, находится внутри приготовился инициализации-список. Вы можете воспользоваться этим, поместив расширение в список инициализаторов фиктивного массива:
template<typename... Args> static void foo2(Args &&... args) { int dummy[] = { 0, ( (void) bar(std::forward<Args>(args)), 0) ... }; }чтобы объяснить содержание инициализатора более подробно:
{ 0, ( (void) bar(std::forward<Args>(args)), 0) ... }; | | | | | | | | | --- pack expand the whole thing | | | | | | --perfect forwarding --- comma operator | | | -- cast to void to ensure that regardless of bar()'s return type | the built-in comma operator is used rather than an overloaded one | ---ensure that the array has at least one element so that we don't try to make an illegal 0-length array when args is emptyдемо.
важное преимущество расширения в
{}заключается в том, что она гарантирует налево-направо оценки.
С C++1z фолд выражения, вы можете просто написать
((void) bar(std::forward<Args>(args)), ...);
пакеты параметров могут быть развернуты только в строго определенном списке контекстов, а оператор
,не является одним из них. Другими словами, невозможно использовать расширение пакета для создания выражения, состоящего из ряда подвыражений, разделенных оператором,.эмпирическое правило " расширение может генерировать список на
,-разделенные моделей, где,это список разделитель.- Оператор,не создает список в грамматическом смысле.чтобы вызвать функцию для каждого аргумента, вы можете использовать рекурсию (которая является основным инструментом в поле variadic template programmer):
template <typename T> void bar(T t) {} void foo2() {} template <typename Car, typename... Cdr> void foo2(Car car, Cdr... cdr) { bar(car); foo2(cdr...); } int main() { foo2 (1, 2, 3, "3"); }
БЕССТЫДНАЯ КОПИЯ [одобрено его источником]
пакеты параметров могут быть развернуты только в строго определенном списке контекстов, а оператор
,не является одним из них. Другими словами, невозможно использовать расширение пакета для создания выражения, состоящего из ряда подвыражений, разделенных оператором,.эмпирическое правило " расширение может генерировать список
,-разделенные модели, где,список разделителей.- Оператор,не строит список в грамматическом смысле.чтобы вызвать функцию для каждого аргумента, вы можете использовать рекурсию (которая является основным инструментом в поле variadic template programmer):
#include <utility> template<typename T> void foo(T &&t){} template<typename Arg0, typename Arg1, typename ... Args> void foo(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){ foo(std::forward<Arg0>(arg0)); foo(std::forward<Arg1>(arg1), std::forward<Args>(args)...); } auto main() -> int{ foo(1, 2, 3, "3"); }
ПОЛЕЗНАЯ НЕ СКОПИРОВАННАЯ ИНФОРМАЦИЯ
еще одна вещь, которую вы, вероятно, не видели в этом ответе, - это использование
&&спецификатор иstd::forward. В C++&&описатель может означать одну из 2 вещей: rvalue-ссылки, или универсальные ссылки.я не буду вдаваться в rvalue-ссылки, но кому-то, кто работает с вариативными шаблонами; универсальные ссылки-это Бог-посылка.
Идеальный Переадресации
одно из применений
std::forwardи универсальные ссылки-это идеальная пересылка типов в другие функции.в вашем примере, если мы передаем
int&доfoo2он будет автоматически понижен доintиз-за подписи сгенерированнойfoo2функция после вычитания шаблона, и если вы хотите, чтобы затем переслать этоargк другой функции, которая изменила бы его ny ссылку, вы получите нежелательные результаты (переменная не будет изменена), потому чтоfoo2будет передаваться ссылка на временный созданный путем передачиintк нему. Чтобы обойти это, мы указываем функцию пересылки, чтобы взять любой типа ссылка переменной (R-значения или lvalue). Затем, чтобы быть уверенным, что мы передаем точный тип, переданный в функции пересылки, которую мы используемstd::forward, затем и только тогда мы допускаем понижение типов; потому что мы сейчас находимся в точке, где это имеет наибольшее значение.Если вам нужно, Прочитайте больше на универсальный ссылок и идеальный переадресации; Скотт Мейерс довольно велик как ресурс.
можно использовать
make_tupleдля расширения пакета, поскольку он вводит контекст, где,последовательность, произведенная расширением, действительнаmake_tuple( (bar(std::forward<Args>(args)), 0)... );Теперь я подозреваю, что неиспользуемый/неназванный / временный кортеж нулей, который создается, детектируется компилятором и оптимизируется
порядок выполнения не гарантируется для этого!
make_tuple( (bar(std::forward<Args>(args)), 0)... );в этом примере параметры будут напечатаны в обратном порядке, по крайней мере с GCC.
foo2(1, 2, 3, "3"); calling bar for 3 calling bar for 3 calling bar for 2 calling bar for 1
Comments