"endl"(或任何输出操纵器)实际上是什么?它是如何实现的以及它如何与 operator<< 一起工作?
What is in reality "endl" (or any output manipulator)? How is it implemented and how does it work with operator<<?
实际上 endl
是什么?当然,它会打印一个新行并刷新 ostream 缓冲区。但它实际上是什么?像endl
这样的"entities"可以由程序员来定义吗?
有这些 "output manipulators" 可以使用库 iomanip
访问,但执行命令时实际发生的情况如下: cout << setprecision(5);
setprecision()
看起来像一个函数调用,但在使用 cout
实例时没有打印任何内容。它改变了精度,但为什么干脆不使用相应的函数成员而不是在代码编写中添加更多"abstraction"?我所说的抽象是指非直观代码。
谢谢!
What is actually endl? Of course, it prints a new line and flushes the ostream buffer. But what is it actually?
std::endl
是一个函数。它
Inserts a newline character into the output sequence os and flushes it
Can such "entities" like endl be defined by the programer?
是的。
这是一个演示程序。
#include <iostream>
std::ostream& test_manip(std::ostream& out)
{
return (out << "In test_manip\n");
}
int main()
{
std::cout << test_manip;
}
及其输出。
In test_manip
What is actually endl
? Of course, it prints a new line and flushes the ostream buffer. But what is it actually?
std::endl
是一个函数,它将 std::ostream&
引用作为输入,returns std::ostream&
引用作为输出:
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& endl( std::basic_ostream<CharT, Traits>& os );
std::basic_ostream::operator<<
有一个重载接受指向这样一个函数的指针:
template<
class CharT,
class Traits = std::char_traits<CharT>
> class basic_ostream : virtual public std::basic_ios<CharT,Traits>
{
...
basic_ostream& operator<<(
std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&)
);
...
};
此重载将调用传递的函数,为其提供调用运算符的 std::ostream
对象,例如:
template<class CharT, class Traits>
basic_ostream<CharT,Traits>& basic_ostream<CharT,Traits>::operator<<(
std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&) )
{
func(*this);
return *this;
}
std::endl
的实现然后可以写入给定的 std::ostream
并刷新它,例如:
template<class CharT, class Traits>
std::basic_ostream<CharT,Traits>& endl( std::basic_ostream<CharT,Traits>& os )
{
os.put(os.widen('\n'));
os.flush();
return os;
}
所以,当你有这样的陈述时:
std::cout << std::endl
它实际上会在内部调用:
std::cout.operator<<(&std::endl)
然后会调用:
std::endl(std::cout)
Can such "entities" like endl be defined by the programer?
是的。任何匹配上述签名 (std::basic_ostream<CharT,Traits>& (*)(std::basic_ostream<CharT,Traits>&)
) 的函数都可以传递给 operator<<
.
what is actually going on when executing a command such as: cout << setprecision(5);
setprecision()
looks like a function call
它是一个函数调用。 I/O 接受用户输入的操纵器与 I/O 不接受任何用户输入的操纵器的工作方式略有不同。
为了将用户输入应用到 std::ostream
对象(或 std::istream
对象),这样的操纵器 returns保存输入的实现定义类型的实例,然后重载非成员 operator<<
以采用该类型。当调用该重载时,它可以根据需要将输入应用到 std::ostream
(或 std::istream
)。
在 std::setprecision()
的情况下,它需要一个 int
作为输入,并且 return 是一个实现定义的类型,它包含 int
,然后type 将 int
传递给 std::ostream::precision()
,例如:
struct PrecisionType { int value; };
PrecisionType setprecision( int n )
{
return PrecisionType{ n };
}
template<class CharT, class Traits>
std::basic_ostream<CharT,Traits>& operator<<( std::basic_ostream<CharT,Traits>& os, const PrecisionType &input )
{
os.precision(input.value);
return os;
}
因此,这样的语句:
std::cout << std::setprecision(5)
实际上会这样调用:
PrecisionType temp = std::setprecision(5);
operator<<(std::cout, temp)
然后将在内部调用:
std::cout.precision(temp.value)
yet nothing is printed when using the cout instance. It changes the precision
正确,因为 set::setprecision()
return 的操纵器不会向 std::ostream
的输出缓冲区写入任何内容,它只是调整 std::ostream
本身。
没有什么可以阻止 I/O 操纵器写入 std::ostream
(或从 std::istream
读取),如果它愿意的话。
why simply not use the corresponding function member instead of adding more "abstraction" to code writing?
您当然可以直接调用成员,但是您将无法使用 <<
链接后续表达式。让每个操纵器 return 引用 std::ostream
(或 std::istream
)被操纵的对象是允许链接的原因。成员方法不return这样的引用。
例如:
cout << setprecision(5) << 123.45 << endl;
翻译为:
operator<<(cout, setprecision(5)).operator<<(123.45).operator<<(&endl);
最终会在内部调用这样的东西:
//operator<<(cout, setprecision(5));
cout.precision(5);
//cout.operator<<(123.45);
use_facet<num_put<char>>(cout.getloc()).put(
ostreambuf_iterator it{cout},
cout,
cout.fill(),
123.45
);
//cout.operator<<(&endl)
endl(cout);
不如使用 <<
重载那么漂亮,是吗?
实际上 endl
是什么?当然,它会打印一个新行并刷新 ostream 缓冲区。但它实际上是什么?像endl
这样的"entities"可以由程序员来定义吗?
有这些 "output manipulators" 可以使用库 iomanip
访问,但执行命令时实际发生的情况如下: cout << setprecision(5);
setprecision()
看起来像一个函数调用,但在使用 cout
实例时没有打印任何内容。它改变了精度,但为什么干脆不使用相应的函数成员而不是在代码编写中添加更多"abstraction"?我所说的抽象是指非直观代码。
谢谢!
What is actually endl? Of course, it prints a new line and flushes the ostream buffer. But what is it actually?
std::endl
是一个函数。它
Inserts a newline character into the output sequence os and flushes it
Can such "entities" like endl be defined by the programer?
是的。
这是一个演示程序。
#include <iostream>
std::ostream& test_manip(std::ostream& out)
{
return (out << "In test_manip\n");
}
int main()
{
std::cout << test_manip;
}
及其输出。
In test_manip
What is actually
endl
? Of course, it prints a new line and flushes the ostream buffer. But what is it actually?
std::endl
是一个函数,它将 std::ostream&
引用作为输入,returns std::ostream&
引用作为输出:
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& endl( std::basic_ostream<CharT, Traits>& os );
std::basic_ostream::operator<<
有一个重载接受指向这样一个函数的指针:
template<
class CharT,
class Traits = std::char_traits<CharT>
> class basic_ostream : virtual public std::basic_ios<CharT,Traits>
{
...
basic_ostream& operator<<(
std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&)
);
...
};
此重载将调用传递的函数,为其提供调用运算符的 std::ostream
对象,例如:
template<class CharT, class Traits>
basic_ostream<CharT,Traits>& basic_ostream<CharT,Traits>::operator<<(
std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&) )
{
func(*this);
return *this;
}
std::endl
的实现然后可以写入给定的 std::ostream
并刷新它,例如:
template<class CharT, class Traits>
std::basic_ostream<CharT,Traits>& endl( std::basic_ostream<CharT,Traits>& os )
{
os.put(os.widen('\n'));
os.flush();
return os;
}
所以,当你有这样的陈述时:
std::cout << std::endl
它实际上会在内部调用:
std::cout.operator<<(&std::endl)
然后会调用:
std::endl(std::cout)
Can such "entities" like endl be defined by the programer?
是的。任何匹配上述签名 (std::basic_ostream<CharT,Traits>& (*)(std::basic_ostream<CharT,Traits>&)
) 的函数都可以传递给 operator<<
.
what is actually going on when executing a command such as:
cout << setprecision(5);
setprecision()
looks like a function call
它是一个函数调用。 I/O 接受用户输入的操纵器与 I/O 不接受任何用户输入的操纵器的工作方式略有不同。
为了将用户输入应用到 std::ostream
对象(或 std::istream
对象),这样的操纵器 returns保存输入的实现定义类型的实例,然后重载非成员 operator<<
以采用该类型。当调用该重载时,它可以根据需要将输入应用到 std::ostream
(或 std::istream
)。
在 std::setprecision()
的情况下,它需要一个 int
作为输入,并且 return 是一个实现定义的类型,它包含 int
,然后type 将 int
传递给 std::ostream::precision()
,例如:
struct PrecisionType { int value; };
PrecisionType setprecision( int n )
{
return PrecisionType{ n };
}
template<class CharT, class Traits>
std::basic_ostream<CharT,Traits>& operator<<( std::basic_ostream<CharT,Traits>& os, const PrecisionType &input )
{
os.precision(input.value);
return os;
}
因此,这样的语句:
std::cout << std::setprecision(5)
实际上会这样调用:
PrecisionType temp = std::setprecision(5);
operator<<(std::cout, temp)
然后将在内部调用:
std::cout.precision(temp.value)
yet nothing is printed when using the cout instance. It changes the precision
正确,因为 set::setprecision()
return 的操纵器不会向 std::ostream
的输出缓冲区写入任何内容,它只是调整 std::ostream
本身。
没有什么可以阻止 I/O 操纵器写入 std::ostream
(或从 std::istream
读取),如果它愿意的话。
why simply not use the corresponding function member instead of adding more "abstraction" to code writing?
您当然可以直接调用成员,但是您将无法使用 <<
链接后续表达式。让每个操纵器 return 引用 std::ostream
(或 std::istream
)被操纵的对象是允许链接的原因。成员方法不return这样的引用。
例如:
cout << setprecision(5) << 123.45 << endl;
翻译为:
operator<<(cout, setprecision(5)).operator<<(123.45).operator<<(&endl);
最终会在内部调用这样的东西:
//operator<<(cout, setprecision(5));
cout.precision(5);
//cout.operator<<(123.45);
use_facet<num_put<char>>(cout.getloc()).put(
ostreambuf_iterator it{cout},
cout,
cout.fill(),
123.45
);
//cout.operator<<(&endl)
endl(cout);
不如使用 <<
重载那么漂亮,是吗?