Search code examples
c++templatesenable-ifredefinition

Avoiding Template operator overload collisions for float and non float on the operator first argument


I have a situation where my template modulus operator does play nice between floats and integer-ish types. As I recall there is a way to make it select the more specific one over the broader scope one, but i can't seem to find or remember how to do this.

Basically I have 2 methods which are coliding (redefinition error.)

What I am trying to do is get the specialized modulus operator overload to be used when its the type (T_COORDS) is float, the the broader one when it's not float.

The specialized one (notice it uses the math fmod function):

template <size_t N
    , enable_if_t<N != 0 && N <= C_COORDS*N_COORDS && N % C_COORDS == 0, bool> = false>
friend Coords operator % (const Coords<float,C_COORDS,N_COORDS>& coords0, const auto (& coords1)[N])
{
  Coords<float,C_COORDS,N_COORDS> result = coords0;
  for (int i=0; i < C_COORDS*N_COORDS; i++)
      result.coords[i] = fmod(result.coords[i], coords1[i%N]);
  return result;
}

The broader one (notice it uses the standard float modulus):

template <std::size_t N
    , std::enable_if_t<N != 0 && N <= C_COORDS*N_COORDS && N % C_COORDS == 0
    , bool> = false>
friend Coords operator % (const Coords<T_COORDS,C_COORDS,N_COORDS>& coords0, const auto (& coords1)[N])
{
  Coords<T_COORDS,C_COORDS,N_COORDS> result = coords0;
  for (int i=0; i < C_COORDS*N_COORDS; i++)
      result.coords[i] %= coords1[i%N];
  return result;
}

Those overloads are enclosed with a Template class definition:

template <class T_COORDS, size_t C_COORDS, size_t N_COORDS>
class Coords {
public:

  T_COORDS coords[C_COORDS*N_COORDS];

  // (body in here)

};

Solution

  • T_COORDS is a class template parameter, not a function template parameter, which means it makes no difference for partial ordering as the more general function is instantiated with T_COORDS=float anyway, and so it's exactly the same as the float one. You can, however, disable that function from overload resolution, utilizing the fact that it already has some enable_if conditions that are dependent on immediate context:

    template <std::size_t N
            , std::enable_if_t<N != 0 && N <= C_COORDS*N_COORDS && N % C_COORDS == 0
                               && !std::is_same<T_COORDS, float>{}
                               // ~~~~~~~~~~~~^
            , bool> = false>
    friend Coords operator %(const Coords<T_COORDS, C_COORDS, N_COORDS>& coords0
                           , const auto (& coords1)[N]);