title: Hidden friend templates in class template
body: <h3>The original working code</h3>
  
  <p>I have a class template with two template parameters and an optimized <code>operator==</code> when the two types are the same and one another condition is satisfied. My original code is as follows (for demonstration purposes I make the generic comparison return <code>false</code> and the one where <code>T1 == T2</code> return <code>true</code>):</p>
  
  <pre class="lang-cpp prettyprint-override"><code>template&lt;typename T1, typename T2&gt;
  struct my_class
  {
      // A few hundred LOCs
  };
  
  template&lt;typename U1, typename U2&gt;
  bool operator==(my_class&lt;U1, U2&gt; const&amp; lhs, my_class&lt;U1, U2&gt; const&amp; rhs)
  {
      return false;
  }
  
  template&lt;typename U&gt;
  auto operator==(my_class&lt;U, U&gt; const&amp; lhs, my_class&lt;U, U&gt; const&amp; rhs)
      -&gt; std::enable_if_t&lt;some_condition, bool&gt;
  {
      return true;
  }
  </code></pre>
  
  <p>The idea is that the first overload of <code>operator==</code> is the default, and when both <code>U1</code> and <code>U2</code> are the same type and <code>some_condition</code> is satisfied the second overload is valid and picked as a better match.</p>
  
  <h3>The problem</h3>
  
  <p>I recently started implementing more and more operators in my generic libraries as <a href="https://www.justsoftwaresolutions.co.uk/cplusplus/hidden-friends.html" rel="nofollow noreferrer">hidden friends</a> to avoid some unwanted implicit conversions and to reduce the overload set the compiler has to choose from at namespace scope.</p>
  
  <p>I first tried the most obvious approach to friends, which is to move the definitions as is in the class template and to prefix them with <code>friend</code>:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>template&lt;typename T1, typename T2&gt;
  struct my_class
  {
      // A few hundred LOCs
  
      template&lt;typename U1, typename U2&gt;
      friend bool operator==(my_class&lt;U1, U2&gt; const&amp; lhs, my_class&lt;U1, U2&gt; const&amp; rhs)
      {
          return false;
      }
  
      template&lt;typename U&gt;
      friend auto operator==(my_class&lt;U, U&gt; const&amp; lhs, my_class&lt;U, U&gt; const&amp; rhs)
          -&gt; std::enable_if_t&lt;some_condition, bool&gt;
      {
          return true;
      }
  };
  </code></pre>
  
  <p>Much not to my surprise this didn't work and I got redefinition errors. The reason why is explained - along with a standard quote - in <a href="https://stackoverflow.com/a/50198379/1364752">this answer</a>.</p>
  
  <h3>An attempt</h3>
  
  <p>We are always comparing <code>my_class</code> with matching template parameters, so I figured that I could get rid of the inner <code>template&lt;typename U1, typename U2&gt;</code> in the first definition, however it is trickier in the second one since the single template parameter was used to create a more specialized overload of <code>operator==</code>. Another solution would be to put that overload in a specialization of <code>my_class&lt;T, T&gt;</code> but since the class is big I didn't feel like duplicating its contents since pretty much everything else is the same. I could probably introduce another layer of indirection for the common code but I already have a hefty amount of indirection.</p>
  
  <p>Failing that I tried to fallback to good old SFINAE to make sure that <code>T1</code> and <code>T2</code> are the same:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>
  template&lt;typename T1, typename T2&gt;
  struct my_class
  {
      // A few hundred LOCs
  
      friend auto operator==(my_class const&amp; lhs, my_class const&amp; rhs)
          -&gt; bool
      {
          return false;
      }
  
      friend auto operator==(my_class const&amp; lhs, my_class const&amp; rhs)
          -&gt; std::enable_if_t&lt;std::is_same&lt;T1, T2&gt;::value &amp;&amp; some_condition, bool&gt;
      {
          return true;
      }
  };
  </code></pre>
  
  <p>For some reason I don't claim to fully understand the second <code>operator==</code> above is actually ill-formed, but we can get around that by adding back some additional defaulted template parameters into the mix:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>template&lt;typename T1, typename T2&gt;
  struct my_class
  {
      // A few hundred LOCs
  
      friend auto operator==(my_class const&amp; lhs, my_class const&amp; rhs)
          -&gt; bool
      {
          return false;
      }
  
      template&lt;typename U1=T1, typename U2=T2&gt;
      friend auto operator==(my_class const&amp; lhs, my_class const&amp; rhs)
          -&gt; std::enable_if_t&lt;std::is_same&lt;U1, U2&gt;::value &amp;&amp; some_condition, bool&gt;
      {
          return true;
      }
  };
  </code></pre>
  
  <p>This compiles as expected, but comparing two instances of <code>my_class</code> with matching <code>T1</code> and <code>T2</code> now returns <code>false</code> because the second overload of <code>operator==</code> is <em>less</em> specialized than the first one. An educated guess tells me that the new template layer is the reason, so I added back template parameters to the first overload of <code>operator==</code> and also an SFINAE condition which is a negation of the other one to make sure that the overload wouldn't be ambiguous with matching <code>T1</code> and <code>T2</code>:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>template&lt;typename T1, typename T2&gt;
  struct my_class
  {
      // A few hundred LOCs
  
      template&lt;typename U1=T1, typename U2=T2&gt;
      friend auto operator==(my_class const&amp; lhs, my_class const&amp; rhs)
          -&gt; std::enable_if_t&lt;not(std::is_same&lt;U1, U2&gt;::value &amp;&amp; some_condition), bool&gt;
      {
          return false;
      }
  
      template&lt;typename U1=T1, typename U2=T2&gt;
      friend auto operator==(my_class const&amp; lhs, my_class const&amp; rhs)
          -&gt; std::enable_if_t&lt;std::is_same&lt;U1, U2&gt;::value &amp;&amp; some_condition, bool&gt;
      {
          return true;
      }
  };
  </code></pre>
  
  <p>This finally gives the intended result while also providing the benefits of hidden friends, but the cost is a bit high in term of readability and maintainability.</p>
  
  <h3>Back to the question I originally meant to ask</h3>
  
  <p>I tried to explain my problem and rough solution and how I got to it above. My question is: is there a better way to achieve the same result (hidden friends with my code) without having to dive into all the template issues I highlighted above? Can I have such hidden friends while relying on the built-in partial ordering of function templates instead of replacing it with another layer of SFINAE as I did? </p>