Какой порядок выполнения операций в fold-expression?

Всем привет! <br/> Я изучаю variadic templates и столкнулся с интересной проблемой. <br/> Вот мой код: <br/> <pre><code class="cpp">#include &lt;iostream&gt;

using namespace std;

void variprint() {}

template&lt;class... Args&gt;
void variprint(Args... args) {
  //cout &lt;&lt; "sizeof: " &lt;&lt; sizeof...(args) &lt;&lt; endl;
  cout &lt;&lt; (args &lt;&lt; ...) &lt;&lt; endl;
}

int main() {
  variprint();
  variprint("test");
  variprint(1, 2);

  return 0;
}</code></pre> <br/> <br/> Ожидаю получить следующий вывод: <br/> <pre><code>test
1 2</code></pre> <br/> <br/> Однако на самом деле вывод выглядит так: <br/> <pre><code>test
4</code></pre> <br/> <br/> Я подозреваю, что проблема может быть связана с приоритетом операций, из-за чего выражение 1 << 2 вычисляется как 4. <br/> Чтобы разобраться, я запустил clang с ключами для промежуточного вывода шаблонов: <br/> <pre><code class="bash">clang.exe -std=c++17 -Xclang -ast-print -fsyntax-only my_file.cpp</code></pre> <br/> <br/> В результате я увидел следующий код в выходном файле: <br/> <pre><code class="cpp">void variprint() {
}
template &lt;class ...Args&gt; void variprint(Args ...args) {
    cout &lt;&lt; "sizeof: " &lt;&lt; sizeof...(args) &lt;&lt; endl;
    cout &lt;&lt; (args &lt;&lt; ...) &lt;&lt; endl;
}
template&lt;&gt; void variprint&lt;&lt;&gt;()template&lt;&gt; void variprint&lt;&lt;const char *&gt;&gt;(const char *args) {
    cout &lt;&lt; "sizeof: " &lt;&lt; sizeof...(args) &lt;&lt; endl;
    cout &lt;&lt; args &lt;&lt; endl;
}
template&lt;&gt; void variprint&lt;&lt;int, int&gt;&gt;(int args, int args) {
    cout &lt;&lt; "sizeof: " &lt;&lt; sizeof...(args) &lt;&lt; endl;
    cout &lt;&lt; args &lt;&lt; args &lt;&lt; endl;
}</code></pre> <br/> <br/> Обратите внимание, никаких скобок...
  • 1 марта 2025 г. 19:54
Ответы на вопрос 2
Проблема, с которой вы столкнулись, связана с тем, как интерпретируется fold-expression в вашем коде. Давайте разберёмся.

Ваша строчка:

```cpp
cout << (args << ...) << endl;
```

интерпретируется как выполнение побитового сдвига (`<<`) над аргументами `args`. Однако, операторы `<<` принимают только два операнда за раз, и когда вы написали `args << ...`, это означает, что каждый аргумент последовательно сдвигается влево на значение следующего аргумента. В результате выражение `1 << 2` становится `4`, и вывод именно этого значения.

Fold-expression применяется к выражению, и в случае операции `<<`, она выполняется таким образом, что все элементы обрабатываются один за другим, каждый раз используя результат предыдущего смещения как базовый аргумент для следующего.

Для того чтобы получить желаемый вывод, вам нужно объединить аргументы с помощью оператора `<<`, а не непосредственно выполнять сдвиг. Вы можете переписать вашу функцию следующим образом, чтобы исправить это:

```cpp
#include <iostream>

using namespace std;

void variprint() {}

template<class... Args>
void variprint(Args... args) {
    ((cout << args << " "), ...);  // Используйте запятую для разделения каждого аргумента
    cout << endl;
}

int main() {
    variprint();
    variprint("test");
    variprint(1, 2);

    return 0;
}
```

Теперь использование `((cout << args << " "), ...)` делает именно то, что нам нужно: для каждого аргумента в `args` он выполняет `cout << args`, разделяя их пробелом. Обратите внимание на использование запятой для разделения аргументов в функции, что является корректным использованием fold-expression для данной задачи.

Таким образом, ваш вывод станет:

```
test
1 2 
```

Проблема, с которой вы столкнулись, наглядно демонстрирует, как использование fold-expressions требует аккуратного обращения с приоритетом операторов и формой записи.
Согласно eel.is/c++draft/expr.prim.fold#nt:fold-operator и eel.is/c++draft/temp.variadic#14 чтобы получить то что ты хотел (и что стандарт называет термином "binary left fold") вместо cout << (args << ...) << endl; надо было написать (cout << ... << args) << endl; 

почему ж в результате оно себя ведёт так, как будто скобки есть??

Потому что у тебя получился binary right fold.
Похожие вопросы