模板类中的朋友操作员声明

时间:2012-08-17 20:55:45

标签: c++ c++11

我对模板类中声明的友元运算符以及类的显式实例化有一个很好的问题:

template<class T>
class IneqCmp{
    friend bool operator!=(T a, T b){
        return !(a==b);
    }
};

struct IntCont{
    int x;
    bool operator==(IntCont that) const{
        return x==that.x;
    }
};
template class IneqCmp<IntCont>;
int main(){
    IntCont a{1}, b{2};
    while(a!=b);
}

我认为operator!=(IntCont,IntCont)应该在IneqCmp&lt; IntCont&gt;的实例化时实例化。但事实并非如此。为什么呢?

1 个答案:

答案 0 :(得分:6)

上面代码的问题不在于operator!=是否定义,而是查找 * 永远不会找到它。

当你将一个函数声明为一个类的朋友时,声明是 strange ,因为它声明了一个名称空间级函数,但它的声明只能通过封闭的ADL获得。类型。由于您的operator!=不会将IneqComp作为两个参数中的任何一个,因此实际上无法通过查找找到它。

如果你想要做的是提供一组类型可用的operator!=的默认实现,你可以用不同的方式解决问题。

首先,您可以使用继承将IneqCmp添加到ADL集:

struct IntCont : IneqCmp<IntCont> {
   // ...
};

因为ADL在参数的类中以及在基类中查找,这将有效地触发IneqCmp<IntCont>内的查找,并且将找到friend声明,从而找到自由函数。

另一种选择是添加一个命名空间,其中将定义泛型operator!=(否则将无法找到)和tag类型。然后从该标签继承:

namespace inequality_comparable {
   struct tag {};
   template <typename T>
   bool operator!=( T const & a, T const & b ) {
      return !(a==b);
   }
}
struct IntCont : inequality_comparable::tag {
};
bool operator==( IntCont const & a, IntCont const & b ) {
   return ...;
}
int main() {
    IntCont a,b;
    std::cout << a != b << "\n";
}

这里的诀窍是因为inequality_comparable::tag是你的类型的基础,它会将命名空间inequality_comparable添加到查找中。当编译器遇到a != b中的main时,它将尝试使用当前作用域中定义的operator!=,类IntCont及其基础和名称空间{{1} }和它的基础是定义的。


* 如果您想验证是否实际生成了运算符,请尝试添加它:

IntCont

如果由于bool operator!=(IntCont a, IntCont b){ return !(a==b); } 的实例化而定义了运算符,则会触发编译器错误。或者,您可以编译并检查生成的符号文件。