0

I have this code in header

class MyMathUtils {
   template <typename T> static bool IsEqual(const T & val1, const T & val2);
};


template <typename T>
bool MyMathUtils::IsEqual(const T & val1, const T & val2)
{
    return (val1 == val2);
};

And this in cpp

template <>
bool MyMathUtils::IsEqual<float>(const float & val1, const float & val2)
{
    if ((val1 - val2) < -EPSILON) return false;
    if ((val1 - val2) > EPSILON) return false;
    return true;
}

Problem is, that compiler gives me this error:

MyMath::MyMathUtils::IsEqual(float const &,float const &)" (??$IsEqual@M@MyMathUtils@MyMath@@SA_NABM0@Z) already defined in MyMathUtils.obj; second definition ignored

But if I use the same, but instead of float I put double, it is compiled correctly. What is incorrect here, that I am missing?

Martin Perry
  • 9,232
  • 8
  • 46
  • 114
  • Include the .cpp file at the end of the header – Lorah Attkins Dec 23 '14 at 10:58
  • Is the float specialization *only* used in the source file you've defined it in? I'm doubting it. And are the values you're passing this coming from *literal constants* (I.e. `obj->IsEqual(1.0,1.0)`) ? those aren't `float` if so; they're `double`. – WhozCraig Dec 23 '14 at 10:58
  • @NorahAttkins: No, explicit specialisations are subject to the ODR, so that will give similar errors unless you declare it `inline`. (Also, treating a file named as source file as if it were a header would be rather confusing.) – Mike Seymour Dec 23 '14 at 11:02
  • 1
    As far as I can see, the ["duplicate"](http://stackoverflow.com/questions/495021) doesn't answer this question. This is about an explicit specialisation, which doesn't need to be implemented in a header. – Mike Seymour Dec 23 '14 at 11:05
  • @MikeSeymour Vandevoorde & Josuttis mention it when explaining the c++ inclusion model (I'm more than sure you already know this - "C++ Templates the complete guide" is a very popular book). I too don't think it's a clear solution, that's why I posted it in a comment. The part on inlining is something I'll have to look into again. – Lorah Attkins Dec 23 '14 at 11:10

1 Answers1

1

You need to declare the specialisation in the header.

namespace MyMathUtils {
   // generic template (shouldn't be static)
   template <typename T> bool IsEqual(const T & val1, const T & val2);

   // explicit specialisation
   template <> bool IsEqual<float>(const float & val1, const float & val2);

   // alternatively, overloading would be simpler
   bool IsEqual(float, float);
};

(I took the liberty of changing this to a namespace, not a class, since that makes much more sense. If you really do want it to be a class for some reason, you'll have to declare the specialisation in the namespace outside.)

Otherwise, using it in other translation units will cause it to be instantiated from the generic template, since the compiler doesn't know that the explicit specialisation exists. This causes multiple definitions.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644