我已经研究过并发现当你想为cout重载输出流操作符时,那么正确的方法是这样做:
std::ostream& operator<<(std::ostream& os, const T& obj)
这个函数必须在类之外定义,因为这里发生的是运算符&lt;&lt;实际上是在ostream中定义的友元函数,你正在利用它。但是,问题是,这个函数究竟是如何在ostream中定义的?由于此函数需要2个参数,而第二个参数是用户定义的,因此他们无法猜出将要发生什么。
特定类的重载应如下所示:
std::ostream& operator<<(std::ostream& os, const MyClass& obj)
编译器/库如何为第二个参数采用泛型定义,特别是因为在C ++中没有泛型类(如Java中的Object)这样的东西?
答案 0 :(得分:6)
我觉得你很困惑:
运营商&lt;&lt;实际上是ostream中定义的友元函数,你正在利用它。
在operator <<
内定义 class std::ostream
是正确的。实际上,它内部定义了几个版本。但那些与我们无关。此外,它们不是friend
函数:根据定义,friend
函数在之外定义它所属的类。
但这与您定义的函数无关,因为您的operator <<
函数是单独的重载,当您将类型的对象作为第二个参数传递给调用<<
(第一个参数为std::ostream&
)。
正如TemplateRex所解释的那样,找到适当函数的确切方法是通过参数依赖查找。但更基本的是,您只需要知道有两种方法可以为给定的参数类型A
和B
定义二元运算符:
A
内的成员函数,并且只有一个B
类型的参数A
和B
。使用运算符时,这两个定义都是候选函数。 (但是有几个运算符,例如复制赋值只能在类中定义,而不能在外部定义)。所以,回到你的问题:
这个函数究竟是如何在ostream中定义的?由于此函数需要2个参数,而第二个参数是用户定义的,因此他们无法猜出即将发生的事情
答案是在ostream
中定义 。唯一的定义是你的,它在外面。
答案 1 :(得分:2)
C ++函数可以重载,即具有相同名称但采用不同参数的多个函数可以共存。编译器经历了名称查找,参数 - 演绎和过载分辨率的三步过程。最后,只有一个函数重载可以作为最佳匹配存活。可以从系列Core C++ by Stephan T. Lavavej中的前三个视频中轻轻地介绍这些概念。
一个常见的情况是命名空间S
(可能是全局的)中的用户定义的类N
,在同一命名空间内有operator<<(ostream&, S const&)
重载。
namespace N {
class S
{
// bla
};
std::ostream& operator<<(std::ostream& os, S& const& obj)
{
// print in terms of public interface of S
// (else, deckare this a friend function inside S)
return os;
}
} // N
int main()
{
std::cout << S(); // operator<<(ostream&, S const&) is the best match
}
名称查找是微妙的,在这种情况下,它通过所谓的参数依赖查找来工作,它查找与函数参数关联的名称空间。对于上面的代码,这些命名空间是std
(所有标准库函数都存在)和N
(您的重载operator<<(ostream&, S const&)
所在的位置)。参数推导将推导出正确的类型,并且重载决策将发现您的重载是最佳匹配(在这种情况下,很可能是唯一的匹配)。
因此,能够使用“本机”语法打印用户定义的类型。
注意:在这种情况下,它不被称为“左移”运算符而是“流插入”运算符,即使后者具有与前者相同的词法形式。