std::endl是一个函数模板,其签名为:

template

std::basic_ostream& endl(std::basic_ostream&);

std::basic_ostream::operator<<重载了std::basic_ostream>::operator<<(std::basic_ostream& (*func)(std::basic_ostream&)),接受某个特定签名的函数。

当您执行std::cout << std::endl时,会在std::endl上执行重载决议,以确定std::endl的正确模板类型并实例化函数。然后该函数衰减为一个指针,并传递给operator<<。

std::basic_ostream::operator<<然后在相应的ostream上调用该函数,并返回返回值。类似于:

template

std::basic_ostream&

std::basic_ostream::operator<<(

std::basic_ostream& (*func)(std::basic_ostream&)

) {

return func(*this);

}

但具体实现由编译器库的编写者决定1。

std::endl会导致换行并告诉ostream进行刷新。您可以通过以下两行代码模拟执行 std::cout << std::endl;:

std::cout.put(std::cout.widen('\n'));

std::cout.flush();

std::endl的具体实现取决于编译器,但上述内容是你可能会写的一个不错的近似(当然是在通用流上)。

如果你包含了 #include ,那么你保证可以访问到 std::endl。如果你包含了std库中的任何其他头文件,则可能也可以访问它。定义它的确切文件也取决于实现。

std::endl被称为“io操纵符”。该技术旨在允许通过链接在一起的<<调用,在输出命令中设置操作io流状态的函数。

要创建自己的io操纵符,如果您希望它与单个类型的ostream一起使用,则只需创建一个通过引用获取该类型的ostream的函数,并通过引用返回它。 这样就成为了一个io操纵符。

如果您想处理一组流,请创建一个类似于:template

std::basic_ostream& bob(std::basic_ostream& os)

{

return os << os.widen('b') << os.widen('o') << os.widen('b');

}

现在它是一个IO操纵器,可以打印"bob"。它可以对相关的basic_ostream执行任何你想要的操作。

另一个计划是这样的:

struct bob_t {

template

OS& operator()(OS& os)const {

return os << os.widen('b') << os.widen('o') << os.widen('b');

}

template

operator OS&(*)(OS&)() const {

return [](OS& os)->OS&{ return bob_t{}(os); };

}

};

static const bob_t bob;

其中 bob 现在是一个可以用作 io 操纵器的对象。

1 这个 << 重载是一个类型为 A->(A->A)->A 的函数。基本上,我们不再将 X 传递给 f,而是将 X 和 f 一起传递给 <<,然后做 f(X)。这只是纯粹的语法糖。

std::endl 是一个模板的事实意味着由于此技术,完美地转发它有点困难。我最终定义了无状态函数类型 endl_t,带有一个 operator basic_ostream&(*)(basic_ostream&)()const 的重载,这样我就可以通过完美转发代理有时传递重载集合。

然后,我们可以将 f:(A->A) 的整个重载集合传递给 <<,让“下一层”解决重载。