C++ (14/17) Weird bottomless template recursion error when using enums as non-type template parameters

Viewed 258

The following code:

#include <iostream>
#include <type_traits>

enum EnumT{Invalid = 0, Float, N_Types};//enum triggers the problem; works fine with ints

template<typename T, EnumT id_>
struct X
{
  template<EnumT id = id_,
           std::enable_if_t<(id != Float)> * = nullptr>
  constexpr explicit X(T v): val_(v)
  {
    std::cout<<"cnstr..."<<id<<"\n";
  }

  template<EnumT id = id_,
           std::enable_if_t<(id == Float)> * = nullptr>
  constexpr /*explicit*/ X(float v): val_(v)
  {
    std::cout<<"cnstr..."<<id<<"\n";
  }


  template<typename OUT,
           EnumT id = id_,
           std::enable_if_t<(id == Float)> * = nullptr>
  explicit operator OUT() const
  {
    std::cout<<"conv. op....\n";
    return static_cast<float>(val_);
  }
private:
  T val_;
};

using Y = X<float, Float>;

bool operator==(Y const &lhs, Y const &rhs){
    return static_cast<float>(lhs) == static_cast<float>(rhs);
}

fails to compile with g++-5.4 or g++-7.4 (tried -std=c++14, -std=c++17) with what seems to be a bottomless template recursion error:

<source>: In substitution of 'template<EnumT id, std::enable_if_t<(id == Float), void>* <anonymous> > constexpr X<float, Float>::X(float) [with EnumT id = Float; std::enable_if_t<(id == Float), void>* <anonymous> = <missing>]':
<source>:17:48:   recursively required by substitution of 'template<EnumT id, std::enable_if_t<(id == Float), void>* <anonymous> > constexpr X<float, Float>::X(float) [with EnumT id = Float; std::enable_if_t<(id == Float), void>* <anonymous> = <missing>]'
<source>:17:48:   required by substitution of 'template<EnumT id, std::enable_if_t<(id == Float), void>* <anonymous> > constexpr X<float, Float>::X(float) [with EnumT id = Float; std::enable_if_t<(id == Float), void>* <anonymous> = <missing>]'
<source>:26:48:   required by substitution of 'template<class OUT, EnumT id, std::enable_if_t<(id == Float), void>* <anonymous> > X<float, Float>::operator OUT<OUT, id, <enumerator> >() const [with OUT = float; EnumT id = Float; std::enable_if_t<(id == Float), void>* <anonymous> = <missing>]'
<source>:39:34:   required from here
<source>:17:48: fatal error: template instantiation depth exceeds maximum of 900 (use '-ftemplate-depth=' to increase the maximum)
   17 |            std::enable_if_t<(id == Float)> * = nullptr>
      |                                                ^~~~~~~
compilation terminated.
Compiler returned: 1

However, if I replace EnumT with either int or a scoped enumeration, everything compiles fine. Also observe the explicit keyword commented out in the 2nd constructor. If I uncomment it then this version with EnumT also compiles fine.

Anyone could explain what exactly is going on? Thanks in advance.

0 Answers
Related