#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
, а вложенные выражения складываются?
Допустим, мы складываем 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';
На странице со ссылкой ваш № 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
,
Как видите, оба производят одинаковый порядок оценки каждого отдельного аргумента, несмотря на различие в скобках.
Обратите внимание, что в общем случае это не всегда так. Некоторые операторы, такие как +
, не имеют гарантированного порядка оценки, как у оператора запятой. Если бы такой оператор использовался вместо запятой для объединения выражений печати, компилятор мог бы в конечном итоге выбрать вывод отдельных аргументов в любом порядке.