Выражение сгиба с оператором запятой и пакетом параметров шаблона

#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 ++ доктор, ',' это оператор слева направо. Это смысл a, b, c, d имея в виду (((a, b), c),d) не (a, (b, (c, d))), Это важно, если a, b, c, d являются утверждениями.

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

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

11

Решение

Допустим, мы складываем 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';
6

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

На странице со ссылкой ваш № 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,

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

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

4