std::endl
是签名的函数模板:
template<class CharT, class Traits>
std::basic_ostream<CharT,Traits>& endl(std::basic_ostream<CharT,Traits>&);
The std::basic_ostream::operator<<
超载std::basic_ostream<CharT,Traits>>::operator<<(std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&))
接受某个签名的函数。
当你这样做时std::cout << std::endl
,重载决策完成于std::endl
,它确定了正确的模板类型std::endl
并实例化一个函数。然后它衰减为一个指针,并传递给operator<<
.
std::basic_ostream::operator<<
然后调用相关 ostream 上的函数,并返回返回值。就像是:
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
std::basic_ostream<CharT, Traits>::operator<<(
std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&)
) {
return func(*this);
}
But the exact implementation is up to the compiler library writer1.
std::endl
导致打印换行符,然后告诉 ostream 自行刷新。你可以效仿做std::cout << std::endl;
通过这两行代码:
std::cout.put(std::cout.widen('\n'));
std::cout.flush();
究竟如何std::endl
实现取决于编译器,但上面是您如何编写它的一个不错的近似(自然在通用流上)。
保证您可以访问std::endl
if you #include <ostream>
。如果您包含来自std
图书馆。哪个文件准确定义它又取决于实现。
std::endl
被称为“io 操纵器”。该技术的目的是允许通过链接将操作 io 流状态的函数设置为与输出命令“内联”<<
一起打电话。
要创建您自己的,如果您希望它与单一类型的 ostream 一起工作,只需创建一个采用这种类型的函数ostream
通过引用,并通过引用返回它。现在它是一个 io 操纵器。
如果您想处理一组流,请创建一个模板,例如:
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>& bob(std::basic_ostream<CharT, Traits>& os)
{
return os << os.widen('b') << os.widen('o') << os.widen('b');
}
现在是一个 io 操纵器,可以打印"bob"
。它可以做任何你想做的事basic_ostream
有问题。
另一种计划是这样的:
struct bob_t {
template<class OS>
OS& operator()(OS& os)const {
return os << os.widen('b') << os.widen('o') << os.widen('b');
}
template<class OS>
operator OS&(*)(OS&)() const {
return [](OS& os)->OS&{ return bob_t{}(os); };
}
};
static const bob_t bob;
where bob
现在是一个可以用作 io 操纵器的对象。
1 This <<
overload is a function of type A->(A->A)->A
. Basically, instead of passing X to f, we pass X and f to <<
, which then does f(X)
. Pure syntactic sugar.
事实是std::endl
是一个模板意味着完美转发由于这种技术而有点痛苦。我最终定义了无状态函数endl_t
类型,具有operator basic_ostream<CharT,Traits>&(*)(basic_ostream<CharT,Traits>&)()const
过载,所以有时我可以通过完美的转发代理传递过载集。
然后我们可以传递整个重载集f:(A->A)
to <<
,并让“下一层”解决过载问题。