title: How exactly is expansion of a parameter pack evaluated with std::forward?
body: <p>I wanted to better understand <em>parameter pack expansions</em>, so I decided to research a bit and, what once seemed obvious to me, stopped being so obvious after trying to understand what exactly is going on. Let's examine a standard parameter pack expansion with <code>std::forward</code>:</p>
  
  <pre><code>template &lt;typename... Ts&gt;
  void foo(Ts&amp;&amp; ... ts) {
      std::make_tuple(std::forward&lt;Ts&gt;(ts)...);
  }
  </code></pre>
  
  <p>My understanding here is that for any parameter pack <code>Ts</code>, <code>std::forward&lt;Ts&gt;(ts)...</code> will result in a comma-separated list of forwarded arguments with their corresponding type, e.g., for <code>ts</code> equal <code>1, 1.0, '1'</code>, the function body will be expanded to:</p>
  
  <pre><code>std::make_tuple(std::forward&lt;int&amp;&amp;&gt;(1), std::forward&lt;double&amp;&amp;&gt;(1.0), std::forward&lt;char&amp;&amp;&gt;('1'));
  </code></pre>
  
  <p>And that makes sense to me. Parameter pack expansion, used with a function call, results in a comma-separated list of calls to that function with appropriate arguments.</p>
  
  <p>What seems to be bothering me is why then would we sometimes need to introduce the comma operator (<code>operator,</code>), if we want to call a bunch of functions in a similar manner? Seeing <a href="https://stackoverflow.com/a/54913886/7151494">this answer</a>, we can read this code:</p>
  
  <pre><code>template&lt;typename T&gt;
  static void bar(T t) {}
  
  template&lt;typename... Args&gt;
  static void foo2(Args... args) {
      (bar(args), ...); // &lt;- notice: comma here
  }
  
  int main() {
      foo2(1, 2, 3, "3");
      return 0;    
  }
  </code></pre>
  
  <p>followed by information that it will result in the following expansion:</p>
  
  <pre><code>(bar(1), bar(2), bar(3), bar("3"));
  </code></pre>
  
  <p>Fair, makes sense, but... <em>why</em>? Why doing this, instead:</p>
  
  <pre><code>template&lt;typename... Args&gt;
  static void foo2(Args... args) {
      (bar(args)...); // &lt;- notice: no comma here
  }
  </code></pre>
  
  <p>doesn't work? According to my logic ("<em>Parameter pack expansion, used with a function call, results in a comma-separated list of calls to that function with appropriate arguments</em>"), it should expand into:</p>
  
  <pre><code>(bar(1), bar(2), bar(3), bar("3"));
  </code></pre>
  
  <p>Is it because of <code>bar()</code> returning <code>void</code>? Well, changing <code>bar()</code> to:</p>
  
  <pre><code>template&lt;typename T&gt;
  static int bar(T t) { return 1; }
  </code></pre>
  
  <p>changes nothing. I would imagine that it would just expand to a comma-separated list of <code>1</code>s (possibly doing some side effects, if <code>bar()</code> was designed as such). Why does this behave differently? Where is my logic flawed?</p>