【问题标题】:How operator<< works with manipulator functionsoperator<< 如何与操纵器函数一起使用
【发布时间】:2019-10-01 21:23:46
【问题描述】:

在 C++ 中可以编写如下代码:

cout << std::endl;

其中,std::endl 是一个以 cout 作为参数调用的函数。我想知道它是 > 运算符的特殊功能,还是有一个通用规则允许与其他运算符(例如使用 +)做同样的事情?

【问题讨论】:

  • 你确定它是这样工作的吗?我希望cout::operator&lt;&lt; 会被调用。
  • @MakrRansom 被调用,但std::endl 本身就是一个函数模板
  • 警告:一旦你开始走上超载经营的道路,你可能会发现自己过度放纵并到处使用它们。尽管您可能会觉得它们很有趣,但您的同事可能会认为您已经疯了。在该路径的底部,您会发现 Boost SpiritBoost Assignment 之类的东西——非常酷,是的,但不是我所说的惯用 C++。

标签: c++ stl


【解决方案1】:

这并没有什么特别之处。 std::ostream::operator&lt;&lt; 被简单地重载以接受各种类型的输入。

在操纵器函数的情况下,operator&lt;&lt; 被重载以默认接受某些类型的函数指针作为输入:

basic_ostream& operator<<(
    std::ios_base& (*func)(std::ios_base&) );

basic_ostream& operator<<(
    std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) );

basic_ostream& operator<<(
    std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&) );

然后这些重载的实现只是调用传递给它们的任何函数,指定*this作为函数的输入参数,例如:

basic_ostream& basic_ostream::operator<<(std::ios_base& (*func)(std::ios_base&) )
{
    (*func)(*this);
    return *this;
}

basic_ostream& basic_ostream::operator<<(std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&))
{
    (*func)(*this);
    return *this;
}

basic_ostream& basic_ostream::operator<<(std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&))
{
    (*func)(*this);
    return *this;
}

因此cout &lt;&lt; std::endl; 只是调用cout.operator&lt;&lt;(std:endl);,其中std::endl() 匹配上面的第三个重载:

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

&lt;iomanip&gt; 标头中的流操纵器函数的情况下,不从用户代码中获取任何输入参数的操纵器(即std::boolalphastd::showbase 等)通常实现为匹配的独立函数上述重载之一,因此直接将ostream/istream 作为参数传递。但是,从用户代码(即std::resetiosflags()std::setbase() 等)获取输入参数的操纵器被定义为返回“未指定”(即实现定义)类型,其中它们的实现定义了operator&lt;&lt; 和@ 的额外重载987654340@ 用于这些类型,以便它们可以分别接收 ostream/istream 进行操作。

例如:std::fill 可以这样实现:

template< class CharT >
struct fill_t { CharT ch; };

template< class CharT >
fill_t setfill( CharT c )
{
    return fill_t<CharT>{c};
}

template< class CharT >
basic_ostream& operator<<(basic_ostream& out, const fill_t &in)
{
    out.fill(in.ch);
    return out;
}

因此cout &lt;&lt; setfill(' ') 调用等效于fill_t tmp = setfill(' '); operator&lt;&lt;(cout, tmp);

【讨论】:

    【解决方案2】:

    这里没有什么特别的。 std::istreamstd::ostream 分别以

    的形式对 operator &gt;&gt;operator &lt;&lt; 进行重载
    basic_istream& operator>>( std::ios_base& (*func)(std::ios_base&) );
    basic_istream& operator>>( std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) );
    basic_istream& operator>>( basic_istream& (*func)(basic_istream&) );
    
    basic_ostream& operator<<( std::ios_base& (*func)(std::ios_base&) );
    basic_ostream& operator<<( std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) );
    basic_ostream& operator<<( std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&) );
    

    允许类将函数指针作为运算符的右侧。做的时候

    cout << std::endl;
    

    std::endl 衰减为函数指针并调用正确的重载。您可以对任何类型的任何运算符执行此操作。


    如果您确实打算这样做,请考虑最不意外的原则,即不要使用具有完全不同含义的通用语法并将其用作您的自定义重载。

    【讨论】:

      【解决方案3】:

      你当然可以。

      #include <iostream>
      
      struct Foo
      {
         int f = 0;
      };
      
      Foo operator+(Foo foo, int (*function)())
      {
         return {foo.f + function()};
      }
      
      int one()
      {
         return 1;
      }
      
      int main(int argc, const char* argv[])
      {
         Foo foo {10};
         std::cout << (foo+one).f << std::endl;
      }
      

      输出:

      11
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-05-05
        • 2020-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-26
        相关资源
        最近更新 更多