【问题标题】:Where is endl manipulator definedendl 操纵器在哪里定义
【发布时间】:2016-02-06 18:59:32
【问题描述】:

我们知道endl 是操纵器,它在内部将'\n' 放入缓冲区,然后刷新缓冲区。 endl 定义在哪里?什么是endl,是宏还是函数还是变量还是类还是对象?如何定义自己的endl 操纵器?

cout << "hello" << endl ; /*what is endl and  where it is defined */

【问题讨论】:

  • std::endl 操纵器在&lt;ostream&gt; 标头中定义。
  • 在 endl 的定义中,它似乎是函数,那么为什么当我们将它与 cout 模板一起使用时我们不传递任何参数 std::basic_ostream & endl(std::basic_ostream& os);
  • 我试图澄清这个问题,我认为这里有一个很好的问题,它吸引了 Yakk 的出色回答。

标签: c++ gcc endl


【解决方案1】:

std::endl是签名的函数模板:

template<class CharT, class Traits>
std::basic_ostream<CharT,Traits>& endl(std::basic_ostream<CharT,Traits>&);

std::basic_ostream::operator&lt;&lt; 重载 std::basic_ostream&lt;CharT,Traits&gt;&gt;::operator&lt;&lt;(std::basic_ostream&lt;CharT,Traits&gt;&amp; (*func)(std::basic_ostream&lt;CharT,Traits&gt;&amp;)) 接受某个签名的函数。

当您执行std::cout &lt;&lt; std::endl 时,会在std::endl 上完成重载解析,这会确定std::endl 的正确模板类型并实例化一个函数。然后它衰减成一个指针,并传递给operator&lt;&lt;

std::basic_ostream::operator&lt;&lt; 然后调用相关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);
}

但具体实现取决于编译器库编写者1

std::endl 会打印一个换行符,然后告诉 ostream 刷新自己。你可以通过这两行代码模拟std::cout &lt;&lt; std::endl;

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

std::endl 的具体实现方式取决于编译器,但以上是您可能如何编写它的一个不错的近似值(自然是在通用流上)。

如果您#include &lt;ostream&gt;,则保证您可以访问std::endl。如果您包含 std 库中的任何其他头文件,您可能可以访问它。究竟是什么文件定义它又取决于实现。

std::endl 被称为“io 操纵器”。该技术旨在允许通过将&lt;&lt; 调用链接在一起,将操纵 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');
}

现在是一个打印"bob"的io操纵器。它可以对有问题的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;

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


1 这个&lt;&lt; 重载是A-&gt;(A-&gt;A)-&gt;A 类型的函数。基本上,我们不是将 X 传递给 f,而是将 X 和 f 传递给 &lt;&lt;,然后 f(X)。纯语法糖。

std::endl 是一个模板这一事实意味着,由于这种技术,完美转发它有点痛苦。我最终定义了无状态函数 endl_t 类型,并带有 operator basic_ostream&lt;CharT,Traits&gt;&amp;(*)(basic_ostream&lt;CharT,Traits&gt;&amp;)()const 重载,因此有时我可以通过完美的转发代理传递重载集。

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

【讨论】:

  • 这是一个非常棒的答案,不知道为什么这不是公认的答案。
【解决方案2】:

http://en.cppreference.com/w/cpp/io/manip/endl 说:

在标题&lt;ostream&gt;中定义

template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& endl( std::basic_ostream<CharT, Traits>& os );

如果我想编写自己的 endl 操纵器,我必须编写什么代码?

如果您只想为std::ostream 创建它,只需创建一个接受对std::ostream 的引用并返回一个的函数。如果你想让它通用,你可以把它模板化,比如std::endl

【讨论】:

  • 所以看起来它是函数,如果是函数,那么为什么我们不向它传递任何参数?
  • @NiravsinhParmar 因为它是在接受特定签名函数的重载operator&lt;&lt; 内部完成的。
猜你喜欢
  • 2014-01-07
  • 2013-05-25
  • 1970-01-01
  • 2010-11-11
  • 2015-06-02
  • 1970-01-01
  • 2010-10-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多