title: What exactly happens in this template metaprogramming snippet?
body: <pre><code>template &lt;typename T, typename = void&gt;
  struct IsIterable : std::false_type {};
  
  template &lt;typename T&gt;
  struct IsIterable&lt;
          T,
          decltype(
              std::begin(std::declval&lt;std::add_lvalue_reference_t&lt;T&gt;&gt;()),
              std::end(std::declval&lt;std::add_lvalue_reference_t&lt;T&gt;&gt;()),
              void()
          )
  &gt; : std::true_type {};
  </code></pre>
  
  <p>I <em>thought</em> I understood this.</p>
  
  <p>The first <code>IsIterable</code> is a class template with a default template argument for the second parameter. It obviously is applicable for every type.</p>
  
  <p>The second part is a partial template specialization of <code>IsIterable</code>. Thanks to SFINAE, it is only chosen when <code>T</code> has the member functions <code>begin()</code> and <code>end()</code>.</p>
  
  <p>Here is my first question I just came up with actually: the base template has a default argument, so this means it still has two template parameters. The partial specialization only has one template parameter. So does this mean that "one template parameter" always gets choosen <em>before</em> "two template parameters, one default"?</p>
  
  <p>Also, do I understand it correctly that the first "base" template inherits from <code>false_type</code>, and the partial template specialization "adds" another inheritance level? (so does the partial specializations inheritance hierarchy look like this: <code>false_type</code> > <code>true_type</code> > <code>IsIterable</code>, where the definitions of <code>false_type</code> are hidden by <code>true_type</code>?)</p>
  
  <p>Now onto my actual question. Why does the <code>decltype</code> expression have to evaluate to <code>void</code>? I thought that it wouldn't matter and I could write </p>
  
  <pre><code>template &lt;typename T&gt;
  struct IsIterable&lt;
          T,
          decltype(
              std::begin(std::declval&lt;std::add_lvalue_reference_t&lt;T&gt;&gt;()),
              std::end(std::declval&lt;std::add_lvalue_reference_t&lt;T&gt;&gt;()),
              bool() // **** change here ****
          )
  &gt; : std::true_type {};
  </code></pre>
  
  <p>as well. But this makes it so that the value of <code>IsIterable</code> is <strong>always</strong> false! Why is, when I change the partial specialization from <code>void</code> to <code>bool</code>, always the first template chosen?</p>