Расширение пакета вариационных шаблонов



Я пытаюсь изучить вариативные шаблоны и функции. Я не могу понять, почему этот код не компилируется:



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).

643   5  

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

    Ничего не найдено.