我读过关于I/O操纵器的文章,对此有点困惑。有人指出,正是由于这些操纵器,我们能够使用<lt;和>>。我不太清楚为什么,但我一直认为是对象,比如std::cout,控制了运算符的功能,比如<lt;。
既然I/O操纵器据称是函数,那么函数怎么可能重载/定义运算符呢?:/
具体来说,他们为接受参数的操纵者陈述了以下内容:
这些操纵器定义了它们自己的运算符<lt;或操作员>>执行所请求的操作。
如果这是真的-不是对象(它们所属的类),而是控制运算符的这些辅助函数<lt;以及>>-如何知道在下面的语句中该做什么?
std::cout << "static constructorn";
标准输入和输出流类(std::basic_istream
和std::basic_ostream
)重载成员和非成员operator>>()
和operator<<()
函数。这些是执行实际I/O的功能。机械手是类似std::endl
、std::left
、std::right
、std::boolalpha
等的功能。它们只是对I/O操作员功能提供的功能的补充。它们允许我们控制I/O操作的某些属性,如浮点表示、字段宽度、填充字符等
关于您提供的示例:
std::cout << "static constructorn";
这将调用非成员函数ostream& operator<<(ostream&, const char*);
,该函数执行字符串的实际输出。
操纵器可以这样定义:
std::ostream& print_1_to_10( std::ostream& os ) {
for (int i = 1; i <= 10; i++) {
os << to_string(i);
}
}
标准流类具有operator>>()
和operator<<()
的成员重载,它们将指向具有上述签名的函数的指针作为参数。基本上是这样的:
std::ostream& std::ostream::operator<<(std::ostream&(*pf)(std::ostream&)) {
pf(*this);
return *this;
}
这使我们能够做到:
std::cout << print_1_to_10;
这就是允许操纵器工作的原因。
总之,操纵器只是辅助函数,可以促进某些类型的操作。
还有一种情况,您可能想要类似于操纵器的东西,但除此之外,您还希望将参数传递给该操纵器(即std::cout << print_1_to(10)
)。在这种情况下,通常创建一个包含重载operator<<()
或operator>>()
的类,这取决于您想要做什么
struct print_1_to {
int n;
print_1_to(int n) : n(n) {}
friend std::ostream& operator<<(std::ostream& os, print_1_to const& p) {
for (int i = 1; i <= p.n; i++) {
os << to_string(i);
}
return os;
}
};
您引用的文本措辞不好。
标准标头定义了操纵器和自由函数重载运算符,该运算符的操作数是流和操纵器。操纵器本身不会超载操作员。
操纵器实际上是一个函数模板。可以重载运算符,以便其中一个操作数可以是函数或函数模板。在后一种情况下,重载运算符本身就是一个函数模板。
在上一条语句中,语法x << y
被转换为函数调用operator<<(x, y);
,然后普通的名称查找和重载解析过程以与任何其他函数调用相同的方式解析该调用。