How to define a friend operator definition in a nested class of a template class?

It works:

template<class Tim> struct Bob { struct Dave { Tim t{}; friend bool operator < (const Dave& a, const Dave& b) { return at < bt; } } d; }; 

This does not work:

 template<class Tim> struct Bob { struct Dave { Tim t{}; friend bool operator < (const Dave& a, const Dave& b); } d; }; template<class Tim> bool operator < (const typename Bob<Tim>::Dave& a, const typename Bob<Tim>::Dave& b) { return at < bt; } 

When I try to use it on a map, for example, I get linker errors:

 1>ConsoleApplication1.obj : error LNK2019: unresolved external symbol "bool __cdecl operator<(struct Bob<int>::Dave const &,struct Bob<int>::Dave const &)" ( ??M@YA _NABUDave@ ?$Bob@H @@ 0@Z ) referenced in function "public: bool __thiscall std::less<struct Bob<int>::Dave>::operator()(struct Bob<int>::Dave const &,struct Bob<int>::Dave const &)const " ( ??R?$less@UDave @ ?$Bob@H @@@ std@ @ QBE_NABUDave@ ?$Bob@H @@ 0@Z ) 

.

 int main() { std::map<Bob<int>::Dave, int> v; v[{}]; } 

How can I correctly define this operator outside the class?

+7
c ++
source share
2 answers

Typically, you do such a thing by striking forward - declaring a template class and a friend function, and then providing specialization in the class definition. However, in this case it is not so simple - the presence of a dependent type puts the Tim class in an undetectable context, so the output will not be executed. However, there is a way:

 #include <iostream> #include <type_traits> #include <map> template<class T> struct Bob; template<typename T, typename> bool operator < (const T& a, const T& b); struct DaveTag {}; template<class Tim> struct Bob { struct Dave : DaveTag { Tim t{}; friend bool operator < <Bob<Tim>::Dave, void>(const typename Bob<Tim>::Dave& a, const typename Bob<Tim>::Dave& b); } d; }; template<typename T, typename = typename std::enable_if<std::is_base_of<DaveTag, T>::value>::type> bool operator < (const T& a, const T& b) { return at < bt; } struct X { double t; }; int main() { std::map<Bob<int>::Dave, int> v; v[{}]; // This won't work // X x, y; //bool b = x < y; } 

Basically, what I did here allows the compiler to output the full Bob<Tim>::Dave as a template parameter for operator< . However, it is clear that a simple definition would deduce any types for T , which can lead to some difficult to understand problems. To avoid this, I added a small DaveTag tag class that allows you to prevent instances of our most common operator< for anything other than Dave .

+4
source share

Here is a satisfactory workaround. I did not want to implement the operator in the class for various reasons (definitions are not available at this point, some explicit instances of the templates are here), but I can live with the following:

 struct Bob { struct Dave { Tim t{}; static bool LessThan(const Dave& a, const Dave& b); friend bool operator < (const Dave& a, const Dave& b) { return LessThan(a, b); } } d; }; 

Now the LessThan static function can be implemented outside the class in the usual way.

0
source share

All Articles