C++模板类嵌套子类友元声明触发非模板函数警告问题求解
你写的my_data_struct是类模板,内部的iterator是依附于模板参数T的内嵌类型,每一种T对应的my_data_struct<T>::iterator都是完全独立的类型。
你在外层my_data_struct<T>中写的友元声明friend bool operator==(const iterator l, const iterator r);,在C++语法中会被识别为声明了一个普通非模板函数作为友元,而不是一个和当前T绑定的模板函数实例。编译器会认为你需要为每一种my_data_struct<T>都单独定义一个对应的全局非模板operator==,但你实际没有提供这些非模板函数的定义,只是在iterator内部定义了对应类型的运算符,两者无法匹配,因此触发了-Wnon-template-friend警告。
虽然实际运行时因为ADL(参数依赖查找)可以找到iterator内部定义的运算符,所以能正常运行,但这个警告确实暴露了声明不匹配的问题。
这里提供两种常用的无警告修复方案:
方案1:内部定义hidden friend(最简便)
直接将operator==的定义放在外层my_data_struct<T>的友元声明处,同时让外层类可以访问iterator的私有成员即可,不需要额外的前置声明:
template<typename T> class my_data_struct { // 你的私有成员,运算符可以直接访问 public: class iterator { int _index; // 允许外层类访问iterator的私有成员 friend class my_data_struct<T>; // iterator的其他逻辑 }; // 直接在此处定义友元运算符,属于hidden friend,自动匹配当前T的iterator类型 friend bool operator==(const iterator& l, const iterator& r) { // 既可访问iterator的_index,也可访问my_data_struct<T>的私有成员 return l._index == r._index; } };
这种方式的operator==只会在参数类型匹配时被ADL找到,不会污染全局命名空间,也不会触发任何警告。
方案2:显式声明模板友元(匹配编译器提示的方案)
如果你希望将运算符的定义放在类外,可以按照编译器的提示,提前声明运算符模板,再显式声明对应模板实例为友元:
// 前置声明类模板 template<typename T> class my_data_struct; // 前置声明运算符模板 template<typename T> bool operator==(const typename my_data_struct<T>::iterator& l, const typename my_data_struct<T>::iterator& r); template<typename T> class my_data_struct { // 你的私有成员 public: class iterator { int _index; // 声明对应T的运算符模板实例为友元 friend bool operator==<T>(const iterator& l, const iterator& r); // iterator的其他逻辑 }; // 外层类也声明对应T的运算符模板实例为友元 friend bool operator==<T>(const iterator& l, const iterator& r); }; // 类外实现运算符模板 template<typename T> bool operator==(const typename my_data_struct<T>::iterator& l, const typename my_data_struct<T>::iterator& r) { return l._index == r._index; }
这种写法和编译器提示的修复建议完全一致,明确告诉编译器你要绑定的是对应模板参数的运算符模板实例,不会触发非模板友元的警告。
内容的提问来源于stack exchange,提问作者Jibidabo

