我发现模板友元函数需要像这样向前声明的规则:
template<typename T>
class Rational;
template<typename T>
const Rational<T> operator* (const Rational<T>& lhs, const Rational<T>& rhs);
template<typename T>
class Rational {
public:
friend
const Rational operator *<> (const Rational& lhs, const Rational& rhs);
};
template<typename T>
const Rational<T> operator* (const Rational<T>& lhs, const Rational<T>& rhs)
{
return Rational<T>();
}
int main(void)
{
Rational<int> r;
r = r * r;
return 0;
}
不仅仅是写作
template<typename T>
class Rational {
public:
friend
const Rational operator * (const Rational& lhs, const Rational& rhs);
};
template<typename T>
const Rational<T> operator* (const Rational<T>& lhs, const Rational<T>& rhs)
{
return Rational<T>();
}
并阅读 explanation因为它指出:
The snag happens when the compiler sees the friend lines way up in the class definition proper. At that moment it does not yet know the friend functions are themselves templates; it assumes they are non-templates ...
... this assumption causes the compiler to generate a call to the non-template functions, but the linker will give you an "undefined external" error because you never actually defined those non-template functions.
但在我的理解中r * r
应该实例化
const Rational<int> operator* (const Rational<int>& lhs, const Rational<int>& rhs);
这与成为 Rational<int>
的 friend 有何不同? ?
编译器/链接器能否区分模板函数和非模板函数?
最佳答案
根据语言([temp.fct]/2)的规则:
A non-template function is not related to a function template (i.e., it is never considered to be a specialization), even if it has the same name and type as a potentially generated function template specialization.
对于第二个片段,当Rational<int>
实例化后,其主体中的友元声明引入了一个非模板函数的声明:
const Rational<int> operator*(const Rational<int>&, const Rational<int>&);
程序中不存在这个函数的定义,事实上,operator*
模板甚至没有被实例化,因为它失去了对非模板的重载解析 operator*
.所以从链接器的角度来看,没有operator*
。完全没有。
但即使 operator*
模板已被实例化,导致编译器发出定义
const Rational<int> operator*<int>(const Rational<int>&, const Rational<int>&);
这是一个不同于非模板的函数 operator*
r * r
实际上需要其定义.如果链接器允许 r * r
调用模板特化,它会导致 r * r
调用一个不同于标准规定它应该调用的函数的函数。 (但是,从技术上讲,链接器没有义务发出错误消息,因为这是“不需要诊断”的错误。)
这就是声明 operator*
的原因模板并确保友元声明引用该模板(或其特化)是必要的。
关于c++ - 链接器可以区分模板函数和非模板函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55975027/