这有效:
template<class Tim> struct Bob { struct Dave { Tim t{}; friend bool operator < (const Dave& a,const Dave& b) { return a.t < b.t; } } d; };
这不起作用:
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; }
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[{}]; }
如何在课外正确定义此运算符?
解决方法
你通常会通过向前声明模板类和友元函数然后在类定义中提供特化来做这样的事情.但是,在这种情况下,它并不那么容易 – 具有依赖类型将Tim类放在非推导的上下文中,因此推论将失败.但是,有一种解决方法:
#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; }
基本上,我在这里所做的是让编译器推导出完整的Bob< Tim> :: Dave作为运算符<的模板参数.然而,显然一个简单的定义将允许推断出任何类型的T可能导致一些难以理解的问题.为了避免它,我添加了一个小标签类DaveTag,它允许阻止我们非常通用的运算符的实例化<除了戴夫之外什么都没有.