title: `enable_if` with `enum` template specialization problem
body: <p>I have problems with GCC compiling <code>enable_if</code>s applied to return value of the templated class method. With Clang, I am able to use an expression in <code>enable_if</code> on the <code>enum</code> template argument, while GCC refuses to compile this code.</p>
  
  <p>Here is the problem description, initial code, and its subsequent modifications that try to satisfy me and compilers (unfortunately, not simultaneously).</p>
  
  <p>I have a non-templated class <code>Logic</code> that contains a templated class method <code>computeThings()</code> which has an <code>enum Strategy</code>  as <strong>one</strong> of its template parameters. The logic in <code>computeThings()</code> depends on the compile-time <code>Strategy</code>, so <code>if constexpr</code> is a reasonable way to make an implementation.</p>
  
  <h3>Variant 1</h3>
  
  <pre><code>    #include &lt;iostream&gt;
  class Logic {
  public:
      enum Strategy { strat_A, strat_B };
      // class A and class B are dummy in this example, provided to show that there are several template
      // parameters, and strategy selection effectively results in 
      // partial (not full) templated method specification
      template &lt;class A, class B, Strategy strategy&gt;
      int computeThings();
  };
  
  template &lt;class A, class B, Logic::Strategy strategy&gt;
  int Logic::computeThings() {
      if constexpr(strategy==strat_A)
          return 0;
      else
          return 1;
  }
  
  int main() {
      Logic mylogic;
      std::cout&lt;&lt;mylogic.computeThings&lt;int,int,Logic::strat_A&gt;()&lt;&lt;std::endl; //outputs 0
      std::cout&lt;&lt;mylogic.computeThings&lt;int,int,Logic::strat_B&gt;()&lt;&lt;std::endl; //outputs 1
      return 0;
  }
  </code></pre>
  
  <p>Variant 1 works fine and compiles both in clang and gcc. However, I want to get rid of <code>if constexpr</code> and split <code>computeThings()</code> into two specialized methods based on the chosen <code>Strategy</code>. Reason: the function is performance-critical and contains a lot of code.</p>
  
  <p>So, I am coming up with Variant 2 that uses <code>enable_if</code> applied to the return value.</p>
  
  <h3>Variant 2</h3>
  
  <pre><code>#include &lt;iostream&gt;
  class Logic {
  public:
      enum Strategy { strat_A, strat_B };
  
      template &lt;class A, class B, Logic::Strategy strategy&gt;
      typename std::enable_if_t&lt;strategy==Logic::strat_A,int&gt;
      computeThings();
  
      template &lt;class A, class B, Logic::Strategy strategy&gt;
      typename std::enable_if_t&lt;strategy==Logic::strat_B,int&gt;
      computeThings();
  };
  
  template &lt;class A, class B, Logic::Strategy strategy&gt;
  typename std::enable_if_t&lt;strategy==Logic::strat_A,int&gt;
  Logic::computeThings() {
      return 0;
  }
  
  template &lt;class A, class B, Logic::Strategy strategy&gt;
  typename std::enable_if_t&lt;strategy==Logic::strat_B,int&gt;
  Logic::computeThings() {
      return 1;
  }
  
  int main() {
      Logic mylogic;
      std::cout&lt;&lt;mylogic.computeThings&lt;int,int,Logic::strat_A&gt;()&lt;&lt;std::endl; //outputs 0
      std::cout&lt;&lt;mylogic.computeThings&lt;int,int,Logic::strat_B&gt;()&lt;&lt;std::endl; //outputs 1
      return 0;
  }
  </code></pre>
  
  <p>I am perfectly comfortable with variant 2 (though would appreciate feedback as well). This code compiles fine using AppleClang (and probably Clang, in general) and produces the right results. However, it fails to compile with GCC with the following error (+ the same but for the other method):</p>
  
  <blockquote>
    <p><code>error: prototype for 'std::enable_if_t&lt;(strategy == Logic:: strat_A),int&gt; Logic::computeThings()' does not match any in class 'Logic' Logic::computeThings()</code></p>
    
    <p><code>candidates are: template&lt;class A, class B, Logic::Strategy strategy&gt; std::enable_if_t&lt;(strategy == strat_B), int&gt; Logic::computeThings() computeThings();</code></p>
    
    <p><code>candidates are: template&lt;class A, class B, Logic::Strategy strategy&gt; std::enable_if_t&lt;(strategy == strat_A), int&gt; Logic::computeThings() computeThings();</code></p>
  </blockquote>
  
  <p>So, apparently, using a simple <code>strategy==Logic::strat_A</code> conflicts with GCC. So, I came up with a solution to this that satisfies both clang and gcc, which wraps <code>strategy==Logic::strat_A</code> into a <code>struct</code>:</p>
  
  <h3>Variant 3</h3>
  
  <pre><code>#include &lt;iostream&gt;
  class Logic {
  public:
      enum Strategy { strat_A, strat_B };
  
      template &lt;Logic::Strategy strategy&gt; struct isStratA {
          static const bool value = strategy==Logic::strat_A;
      };
  
      template &lt;class A, class B, Logic::Strategy strategy&gt;
      typename std::enable_if_t&lt;Logic::isStratA&lt;strategy&gt;::value,int&gt;
      computeThings();
  
      template &lt;class A, class B, Logic::Strategy strategy&gt;
      typename std::enable_if_t&lt;!Logic::isStratA&lt;strategy&gt;::value,int&gt;
      computeThings();
  };
  
  template &lt;class A, class B, Logic::Strategy strategy&gt;
  typename std::enable_if_t&lt;Logic::isStratA&lt;strategy&gt;::value,int&gt;
  Logic::computeThings() {
      return 0;
  }
  
  template &lt;class A, class B, Logic::Strategy strategy&gt;
  typename std::enable_if_t&lt;!Logic::isStratA&lt;strategy&gt;::value,int&gt;
  Logic::computeThings() {
      return 1;
  }
  
  int main() {
      Logic mylogic;
      std::cout&lt;&lt;mylogic.computeThings&lt;int,int,Logic::strat_A&gt;()&lt;&lt;std::endl; //outputs 0
      std::cout&lt;&lt;mylogic.computeThings&lt;int,int,Logic::strat_B&gt;()&lt;&lt;std::endl; //outputs 1
      return 0;
  }
  </code></pre>
  
  <p>with Variant 3, both Clang and GCC are happy. However, I am not, as I have to create a lot of dummy wrappers for an unknown reason (here, I have just one, but technically, I should have both <code>isStratA&lt;&gt;</code> and <code>isStratB&lt;&gt;</code>).</p>
  
  <p>Questions:</p>
  
  <ul>
  <li>do I violate any C++ standard (or common sense) in my Variant 2?</li>
  <li>do I have an easy way of making Variant 2-type solution working without going to dummy wrappers as in Variant 3?</li>
  </ul>
  
  <p>(if that matters, GCC 7.4.0 and Apple LLVM version 10.0.0: clang-1000.11.45.5)</p>