title: Failed template argument deduction with partially specialised template using 'auto'
body: <p>For a bit of fun, I created a very basic compile-time type-value map class, as follows:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>template &lt;typename T, auto V&gt;
  struct TypeValuePair { };
  
  template &lt;typename... TypeValuePairs&gt;
  struct TypeValueMap
    {
    struct MapItems : TypeValuePairs... { };
  
    template &lt;typename T, auto V&gt;
    static constexpr auto Lookup(TypeValuePair&lt;T, V&gt;*)
      { return V; }
  
    template &lt;auto V, typename T&gt;
    static T Lookup(TypeValuePair&lt;T, V&gt;*);
  
    template &lt;typename T&gt;
    static constexpr auto ValueFor = Lookup&lt;T&gt;((MapItems*)nullptr);
  
    template &lt;auto V&gt;
    using TypeFor = decltype(Lookup&lt;V&gt;((MapItems*)nullptr));
    };
  </code></pre>
  
  <p>to be used in a way such as this:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>struct A; struct B; struct C;
  enum class Values { A, B, C };
  
  using Map = TypeValueMap&lt;
                  TypeValuePair&lt;A, Values::A&gt;,
                  TypeValuePair&lt;B, Values::B&gt;,
                  TypeValuePair&lt;C, Values::C&gt;,
                  TypeValuePair&lt;struct Other, 0&gt;
                &gt;;
  
  static_assert(Map::ValueFor&lt;A&gt; == Values::A, "");
  static_assert(Map::ValueFor&lt;B&gt; == Values::B, "");
  static_assert(Map::ValueFor&lt;C&gt; == Values::C, "");
  static_assert(Map::ValueFor&lt;struct Other&gt; == 0, "");
  
  static_assert(std::is_same&lt;Map::TypeFor&lt;Values::A&gt;, A&gt;::value, "");     //***
  static_assert(std::is_same&lt;Map::TypeFor&lt;Values::B&gt;, B&gt;::value, "");
  static_assert(std::is_same&lt;Map::TypeFor&lt;Values::C&gt;, C&gt;::value, "");
  static_assert(std::is_same&lt;Map::TypeFor&lt;0&gt;, struct Other&gt;::value, "");  //***
  </code></pre>
  
  <p>Unfortunately, the two lines marked <code>//***</code> fail with the error <em>failed template argument deduction</em> or similar on clang and g++ (the two compilers I have to hand).  I can understand why this might be because <code>Values::A</code> has the value <code>0</code> so the two potentially collide.  However, I would argue that they are in fact different types – one is plain integer, the other an <code>enum class</code> with underlying type integer – and so shouldn't in fact collide.</p>
  
  <p>If I implement my map class differently, like so:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>template &lt;typename T, auto V&gt;
  struct TypeValuePair
    {
    protected:
    static constexpr auto Lookup(T*)
      { return V; }
  
    template &lt;template &lt;auto&gt; class Wrapper&gt;
    static T Lookup(Wrapper&lt;V&gt;*);
    };
  
  template &lt;typename... TypeValuePairs&gt;
  struct TypeValueMap
    {
    struct MapItems : TypeValuePairs...
      { using TypeValuePairs::Lookup...; };
  
    template &lt;auto&gt; struct LookupByValue;
  
    template &lt;typename T&gt;
    static constexpr auto ValueFor = MapItems::Lookup((T*)nullptr);
  
    template &lt;auto V&gt;
    using TypeFor = decltype(MapItems::Lookup((LookupByValue&lt;V&gt;*)nullptr));
    };
  </code></pre>
  
  <p>then there are no template argument deduction errors.</p>
  
  <p>Therefore the question is, is the failure to deduce the template argument in the first implementation due to a bug in the compilers (given my assertion that integer and <code>enum class</code> should be treated as different types and not collide) or is it a misunderstanding on my side of what is possible with template argument deduction (I am not a language lawyer!), or some other bug in my implementation?</p>