title: Variadic templates transforming between tuples and lists for function calls
body: <p>The intent is to transform variadic template arguments from pair wise argument to list wise arguments; I mean like:</p>
  <pre><code>concept NameT = ...
  concept ValueT = ...
  
  template&lt;NameT ...ArgName, ValueT ...ArgValue&gt;
  Item(ArgName&amp;&amp;... arg_name, ArgValue&amp;&amp;... arg_value) { ... }
  
  std::vector&lt;Item&gt; items;
      
  template&lt;typename... Args&gt;
  void collect(Args&amp;&amp; ...args) {
     items.emplace_back(arg1_name, arg2_name,..., arg1_value, arg2_value,...):
  }
  
  // usage like:
  collect(arg1_name, arg1_value, arg2_name, arg2_value, ...);
  </code></pre>
  <p>But before I'll go into details, I'll show my testing/concept which is not working as expected, see <a href="https://godbolt.org/z/K3o3vvPf8" rel="nofollow noreferrer">godbolt</a></p>
  <pre class="lang-cpp prettyprint-override"><code>template&lt;typename T&gt; concept NameT = std::convertible_to&lt;T, std::string_view&gt;;
  template&lt;typename T&gt; concept ValueT = std::convertible_to&lt;T, int&gt;;
  
  struct Item {
      template&lt;NameT ...ArgName, ValueT ...ArgValue&gt;
      Item(ArgName&amp;&amp;... arg_name, ArgValue&amp;&amp;... arg_value)
      {
          ((std::cout &lt;&lt; std::forward&lt;ArgName&gt;(arg_name) &lt;&lt; &quot; : &quot; &lt;&lt; std::forward&lt;ArgValue&gt;(arg_value) &lt;&lt; '\n'), ...);
      }
  };
  
  int main() {
      auto const v1 = Item(&quot;arg1&quot;, 42);
      return 0;
  }
  </code></pre>
  <p>results into:</p>
  <pre><code>candidate template ignored: constraints not satisfied [with ArgName = &lt;&gt;, ArgValue = &lt;const char (&amp;)[5], int&gt;]
  </code></pre>
  <p>which I wouldn't really expect (empty ArgName constraint).</p>
  <p>So I wrote a small helper to retain the API of name/value arg pairs - of <code>collect()</code> and here I am, see <a href="https://godbolt.org/z/bGjx3nrdT" rel="nofollow noreferrer">godbolt</a></p>
  <pre class="lang-cpp prettyprint-override"><code>constexpr auto MAX_ARGS = 2;
  
  template&lt;typename T&gt; concept ArgNameT = std::convertible_to&lt;T, std::string_view&gt;;
  template&lt;typename T&gt; concept ArgValueT = std::convertible_to&lt;T, int&gt;;
  
  struct item_value {
      int value; // becomes variant
      friend std::ostream&amp; operator&lt;&lt;(std::ostream&amp; os, item_value ival) {
          os &lt;&lt; ival.value;
          return os;
      }
  };
  
  struct item {
      template&lt;typename... ArgNames, typename... ArgValues&gt;
      item(std::string_view item_name_, ArgNames&amp;&amp; ...arg_names_, ArgValues&amp;&amp; ...arg_values_) 
      : item_name{ item_name_ }
      , arg_names{ std::forward&lt;ArgNames&gt;(arg_names_)...}
      , arg_values{ std::forward&lt;ArgValues&gt;(arg_values_)...}
      {}
      std::string item_name;
      // This memory layout to minimize footprint
      std::array&lt;std::string_view, MAX_ARGS&gt; arg_names;
      std::array&lt;item_value, MAX_ARGS&gt; arg_values;
  };
  
  struct logger {
      static logger&amp; instance() {
          static logger static_instance;
          return static_instance;
      };
      template&lt;typename... Args&gt; void add_item(std::string_view name, Args&amp;&amp; ...args);
      template&lt;typename... Args&gt; void do_add_item(std::string_view name, int payload, Args&amp;&amp; ...args);
  
      using arg_type = struct { std::string_view name; item_value value; };
  
      auto make_arg_types() { return std::tuple&lt;&gt;(); }
  
      template&lt;ArgNameT FirstName, ArgValueT FirstValue, typename... Args&gt;
      auto make_arg_types(const FirstName &amp;name, const FirstValue &amp;value, const Args&amp;... args) {
          return std::tuple_cat(
              std::make_tuple(arg_type{name, value}), 
              make_arg_types(std::forward&lt;Args&gt;(args)...)
          );
      }
  
      std::vector&lt;item&gt; items;
  };
  
  // implementation
  template&lt;typename... Args&gt;
  void logger::add_item(std::string_view item_name, Args&amp;&amp; ...args) {
      int payload = 69;
      do_add_item(item_name, payload, std::forward&lt;Args&gt;(args)...);
  }
  template&lt;typename... Args&gt;
  void logger::do_add_item(std::string_view item_name, int payload, Args&amp;&amp; ...args) {
  
      auto arg_types = make_arg_types(std::forward&lt;Args&gt;(args)...);
      // so far, we have arg name/value tuples ...
      std::apply([](auto&amp;&amp;... arg_type) {
          ((std::cout &lt;&lt; arg_type.name &lt;&lt; &quot; : &quot; &lt;&lt; arg_type.value &lt;&lt; '\n'), ...);
      }, arg_types);
  
      // ... but item's CTor API is different
      //items.emplace_back(item_name, payload, ..., ...); // How to?
  }
  
  template&lt;typename... Args&gt;
  void collect(std::string_view item_name, Args&amp;&amp; ...args) { // free function
      logger::instance().add_item(item_name, std::forward&lt;Args&gt;(args)...);
  }
  
  int main() {
      collect(&quot;Superman&quot;); // no arg
      collect(&quot;Batman&quot;, &quot;is bad&quot;sv, 666); // single arg
      // ... 2 name/value args etc.
  
      return 0;
  }
  </code></pre>
  <p>The problem with the <code>collect()</code>'s API ist solved, but <code>item</code> class constructor argument is different (... and rises as new problem), hence I can't emplace them into logger's vector <code>items</code>.</p>
  <p>How to emplace them?</p>