Выражение сгиба с оператором запятой и набором параметров вариабельного шаблона - PullRequest
0 голосов
/ 16 ноября 2018
#include<iostream>
using namespace std;

template<typename ...Args>
void output_argus(Args&&... args) 
{
    ((cout << args << '\n'), ...);    // #1
    (... , (cout << args << '\n'));   // #2
}


int main()
{
    output_argus(1, "test", 5.6f);
}

На основе оператора c ++ doc , ',' - это оператор слева направо . Это означает a, b, c, d означает (((a, b), c),d), а не (a, (b, (c, d))). Это важно, если a, b, c, d - операторы.

Однако, на основе выражения сгиба doc , для ',', который должен использовать одинарный левый сгиб .

Мой вопрос, почему оба оператора в моем коде работают? Разве не должен работать только № 2? А также как понимать ... и args. а выражение вложенного сгиба?

Ответы [ 2 ]

0 голосов
/ 16 ноября 2018

Допустим, мы складываем 3 выражения над бинарным оператором с одинарным сгибом. У нас есть два варианта: (xs @ ...) (одинарный правый сгиб) и (... @ xs) (одинарный левый сгиб).

(xs @ ...) расширяется до (a @ (b @ c))

(... @ xs) расширяется до ((a @ b) @ c)

Что можно сказать о разнице между выражениями a @ (b @ c) и (a @ b) @ c? Если @ является ассоциативным для этих типов, то эти два выражения идентичны. Вот что значит ассоциативный. Если у вас был пакет параметров целых чисел, то унарный левый сгиб по + и унарный правый сгиб по + будут иметь одинаковое значение (переполнение по модулю), поскольку сложение ассоциативно. Вычитание, с другой стороны, не ассоциативно. (xs - ...) и (... - xs) означают совершенно разные вещи.

Аналогично, оператор , в C ++ является ассоциативным. Неважно, каким образом вы заключите в скобки выражения. ((a, b), c) и (a, (b, c)) и оценивают, и отбрасывают a, затем оценивают и отбрасывают b, затем оценивают c, и это результат. Это проще понять, если вы сводите выражения к буквам, почему это так.

В результате оба ((cout << args << '\n'), ...) и (... , (cout << args << '\n')) делают одно и то же, и оба они фактически означают:

cout << args1 << '\n';
cout << args2 << '\n';
// ...
cout << argsN << '\n';
0 голосов
/ 16 ноября 2018

На связанной странице ваш номер 1 расширяется следующим образом:

((cout << args₀ << '\n'), ((cout << args₁ << '\n'), (cout << args₂ << '\n')));

Удаление некоторого повторения, чтобы сделать его чище:

args₀, (args₁, args₂)

Для # 2 расширение сводится к:

(args₀, args₁), args₂

Давайте рассмотрим оценку каждого из них.

# 1:

args₀  ,  (args₁, args₂)
      ^^^

Подчеркнутая запятая оценивает левую сторону, печатая 1.Затем оценивается правая сторона, которая оценивает печать args₁, печать test, затем печать args₂, печать 5.6.

# 2:

(args₀, args₁)  ,  args₂
               ^^^

Подчеркнутая запятая оценивает левую сторону.Это запускает оценку другой запятой, которая оценивает печать args₀, печать 1, затем оценивает печать args₁, печать test.Теперь подчеркнутая запятая завершает оценку левой стороны и вычисляет правую сторону, печатая 5.6.

. Как видите, оба производят одинаковый порядок оценки каждого отдельного аргумента, несмотря на то, что группировка скобок различна.

Обратите внимание, что в общем случае это не всегда так.Некоторые операторы, такие как +, не имеют гарантированного порядка вычисления, как оператор запятой.Если бы такой оператор использовался вместо запятой для объединения выражений печати, компилятор мог бы в конечном итоге выбрать оценку отдельных аргументов в любом порядке.

...