This works:
template<class Tim>
struct Bob
{
struct Dave
{
Tim t{};
friend bool operator < (const Dave& a, const Dave& b)
{
return a.t < b.t;
}
} 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 a.t < b.t;
}
When I try to use it in 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 define this operator correctly outside the class?
You would normally do such a thing by forward-declaring the template class and the friend function and then providing a specialization within the class definition. However, in this case it is not as easy - having a dependent type puts the Tim
class in a non-deduced context, so the deduction will fail. However, there's a way around it:
#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 a.t < b.t;
}
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've done here is let the compiler deduce the full Bob<Tim>::Dave
as the template parameter for operator<
. However, clearly a simple definition would allow for any types to be deduced for T
potentially leading to some difficult-to-understand problems. To avoid it I added a small tag class DaveTag
which allows to prevent instantiations of our very generic operator<
for anything but Dave
.
Here is a satisfactory workaround. I didn't want to implement the operator in the class for various reasons (definitions not available at that point, some explicit template instantiation going on 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 static function LessThan
can be implemented out of the class in the normal way.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.