title: How come std::initializer_list is allowed to not specify size AND be stack allocated at the same time?
body: <p>I understand from <a href="https://stackoverflow.com/questions/61825532/does-stdinitializer-list-heap-allocate-memory">here</a> that <code>std::initializer_list</code> doesn't need to allocate heap memory. Which is very strange to me since you can take in an <code>std::initializer_list</code> object without specifying the size whereas for arrays you always need to specify the size. This is although initializer lists internally almost the same as arrays (as the post suggests).</p>
  <p>What I have a hard time wrapping my head around is that with C++ as a statically typed language, the memory layout (and size) of every object must be fixed at compile time. Thus, every <code>std::array</code> is another <em>type</em> and we just spawn those types from a common template. But for <code>std::initializer_list</code>, this rule apparently doesn't apply, as the memory layout (while it can be derived from the arguments passed to its constructor) doesn't need to be considered for a receiving function or constructor. This makes sense to me only if the type heap allocates memory and only reserves storage to <em>manage</em> that memory. Then the difference would be much like <code>std::array</code> and <code>std::vector</code>, where for the later you also don't need to specify size.</p>
  <p>Yet <code>std::initializer_list</code> doesn't use heap allocations, as my tests show:</p>
  <pre><code>#include &lt;string&gt;
  #include &lt;iostream&gt;
  
  void* operator new(size_t size)
  {
      std::cout &lt;&lt; &quot;new overload called&quot; &lt;&lt; std::endl;    
      return malloc(size);
  }
  
  
  template &lt;typename T&gt;
  void foo(std::initializer_list&lt;T&gt; args)
  {
      for (auto&amp;&amp; a : args)
      std::cout &lt;&lt; a &lt;&lt; std::endl;
  }
  
  int main()
  {
      foo({2, 3, 2, 6, 7});
  
      // std::string test_alloc = &quot;some string longer than std::string SSO&quot;;
  }
  </code></pre>
  <p>How is this possible? Can I write a similar implementation for my own type? That would really save me from blowing my binary whenever I play the compile time orchestra.</p>
  <p><strong>EDIT</strong>: I should note that the question I wanted to ask is not how the compiler knows what size it should instantiate the initializer list with (as can be achieved via template argument deduction) but how it is then not a different type from all the other instantiations of an initializer list (hence why you can pass initializer lists of different sizes to the same function).</p>