5

The following code defined in 'util.h' compiles and links. However when I move the implementation for the operator overloads into 'util.cc', the linker can not resolve the symbols. Is this this possible to do, or can this not be done due to the nature of templates?

Thanks,


Working

util.h

template<class T>
struct Rect {
  T x, y, w, h;

  friend bool operator ==(const Rect<T> &a, const Rect<T> &b) {
    return (a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h);
  }

  friend bool operator !=(const Rect<T> &a, const Rect<T> &b) {
    return !(a == b);
  }
};

Not working

util.h

template<class T>
struct Rect {
  T x, y, w, h;

  friend bool operator ==(const Rect<T> &a, const Rect<T> &b);
  friend bool operator !=(const Rect<T> &a, const Rect<T> &b);
};

util.cc

template<class T>
bool operator ==(const Rect<T> &a, const Rect<T> &b)
{
    return (a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h);
}

template<class T>
bool operator !=(const Rect<T> &a, const Rect<T> &b)
{
    return !(a == b);
}
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
IntelOrca
  • 108
  • 2
  • 8
  • thanks, must of missed this during my searches – IntelOrca Mar 30 '13 at 23:46
  • @AndyProwl but note that the operators are **non-template** functions that are injected into the enclosing namespace of `Rect` as a side-effect of the instantiation process, so the confusion is understandable. – TemplateRex Mar 30 '13 at 23:54
  • @rhalbersma: That's right, I missed it. Thank you for that. Actually, I am wondering if simply moving those operator templates to the header file would work at all then. I believe it shouldn't – Andy Prowl Mar 31 '13 at 00:01
  • @AndyProwl friend functions defined inside class templates are really strange beasts. Vandevoorde & Josuttis 11.7 has a nice piece on it. They behave as non-template functions during overload resolution, but the injected name is only found through ADL, so you cannot use arguments convertible to `Rect` because these are not associated to `Rect` :-) – TemplateRex Mar 31 '13 at 00:08
  • @rhalbersma: Yes, I am aware of this, what I am thinking is that in this case moving those free operator templates to the header file shouldn't help, right? I mean, the class template is actually declaring the *non-template* operators as friends, and those are different from the global operator templates. So the linker should complain about unresolved references eventually, even though the global operator templates are put in a header – Andy Prowl Mar 31 '13 at 00:13
  • @AndyProwl no it won't work because you would be defining non-template functions with a template syntax. – TemplateRex Mar 31 '13 at 00:13
  • @rhalbersma: [This](http://liveworkspace.org/code/48Ihhx$146) is what I meant, and [this](http://liveworkspace.org/code/48Ihhx$148) is how it should be fixed (unless we want to inline the definition of those operators in-class, as the OP originally did) – Andy Prowl Mar 31 '13 at 00:15
  • @AndyProwl yes, so the overload resolution would prefere the non-template version which isn't defined. – TemplateRex Mar 31 '13 at 00:17
  • @rhalbersma: Exactly. But it is still possible to define the operators out-of-class (see the second link) – Andy Prowl Mar 31 '13 at 00:17
  • I have to disagree that this is a duplicate of the question linked. This question deals with templating stand-alone (non-class) operators, while the other discusses templates in header files. – Syndog Aug 30 '16 at 22:12

1 Answers1

6

The answer to your actual question is no for reasons explained in @AndyProwl link, and even if you would have included the operator definitions inside the header util.h the answer would still be no.

The reason is that these operators are actually non-template functions that are injected into the enclosing scope of the class they are defined in as a side-effect of the class template instantiation. In other words, this class template

template<class T>
struct Rect {
  T x, y, w, h;

  friend bool operator ==(const Rect<T> &a, const Rect<T> &b);
  friend bool operator !=(const Rect<T> &a, const Rect<T> &b);
};

generates the following two non-template functions for every type T

bool operator ==(const Rect<T> &a, const Rect<T> &b);
bool operator !=(const Rect<T> &a, const Rect<T> &b);

If you then also define function templates

template<class T>
bool operator ==(const Rect<T> &a, const Rect<T> &b)
{
    return (a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h);
}

template<class T>
bool operator !=(const Rect<T> &a, const Rect<T> &b)
{
    return !(a == b);
}

and call them in your code, then name lookup and argument deduction proceed without difficulty, but function overload resolution will end in a tie which is broken by the non-template in-class friend functions. But when these are not defined, your program will fail to link.

There are two escapes: define the operators in-class (as you already provided) or (again as alluded to by @AndyProwl) to befriend the general operator templates inside the class with this peculair syntax

// forward declarations
template<typename T> struct Rect;
template<typename T> bool operator==(const Rect<T>&, const Rect<T>&);
template<typename T> bool operator!=(const Rect<T>&, const Rect<T>&);

template<class T>
struct Rect {
  T x, y, w, h;

  // friend of operator templates
  friend bool operator == <>(const Rect<T> &a, const Rect<T> &b);
  friend bool operator != <>(const Rect<T> &a, const Rect<T> &b);
};

// definitions of operator templates follow

Note that befriending templates is a tricky business, as explained in this old column by Herb Sutter.

Community
  • 1
  • 1
TemplateRex
  • 69,038
  • 19
  • 164
  • 304