【问题标题】:std::endl is of unknown type when overloading operator<<重载 operator<< 时 std::endl 的类型未知
【发布时间】:2010-11-11 04:29:47
【问题描述】:

我重载了运算符

template <Typename T>
UIStream& operator<<(const T);

UIStream my_stream;
my_stream << 10 << " heads";

有效但:

my_stream << endl;

给出编译错误:

错误 C2678: 二进制 '

使my_stream &lt;&lt; endl 工作的解决方法是什么?

【问题讨论】:

  • 对UIStream一无所知,有点难以评论。
  • 然而,你找到了办法。 :)

标签: c++ stl operator-overloading


【解决方案1】:

std::endl 是一个函数,std::cout 通过实现operator&lt;&lt; 来利用它来获取与std::endl 具有相同签名的函数指针。

在那里,它调用函数,并转发返回值。

这是一个代码示例:

#include <iostream>

struct MyStream
{
    template <typename T>
    MyStream& operator<<(const T& x)
    {
        std::cout << x;

        return *this;
    }


    // function that takes a custom stream, and returns it
    typedef MyStream& (*MyStreamManipulator)(MyStream&);

    // take in a function with the custom signature
    MyStream& operator<<(MyStreamManipulator manip)
    {
        // call the function, and return it's value
        return manip(*this);
    }

    // define the custom endl for this stream.
    // note how it matches the `MyStreamManipulator`
    // function signature
    static MyStream& endl(MyStream& stream)
    {
        // print a new line
        std::cout << std::endl;

        // do other stuff with the stream
        // std::cout, for example, will flush the stream
        stream << "Called MyStream::endl!" << std::endl;

        return stream;
    }

    // this is the type of std::cout
    typedef std::basic_ostream<char, std::char_traits<char> > CoutType;

    // this is the function signature of std::endl
    typedef CoutType& (*StandardEndLine)(CoutType&);

    // define an operator<< to take in std::endl
    MyStream& operator<<(StandardEndLine manip)
    {
        // call the function, but we cannot return it's value
        manip(std::cout);

        return *this;
    }
};

int main(void)
{
    MyStream stream;

    stream << 10 << " faces.";
    stream << MyStream::endl;
    stream << std::endl;

    return 0;
}

希望这能让您更好地了解这些事情的工作原理。

【讨论】:

  • 请在您投反对票时发表评论,以便我改进答案。
  • 我没有投反对票,但这里缺少一个重要的细节:std::endl 不是函数,而是模板函数。这意味着如果您尝试将接受 operatortemplate <typename T> mystream& operator<<( T& (*fp)(T&) )(此签名将接受所有 STL basic_stream&lt;&gt;ios_basebasic_ios&lt;&gt; 操纵器)编译器将无法匹配 std ::endl 针对模板,因为它本身就是一个模板,它无法定义 T 的含义。
  • 谢谢!这有助于我回答另一个问题。 stackoverflow.com/questions/2196155
  • 为什么使用typedef CoutType 而不是仅仅使用ostream
【解决方案2】:

问题是std::endl 是一个函数模板,作为你的操作符&lt;&lt; 是。所以当你写的时候:

my_stream << endl;

您会希望编译器为运算符推导出模板参数 以及endl。这是不可能的。

所以你必须编写额外的、非模板的、运算符&lt;&lt; 的重载来 与机械手一起工作。他们的原型将如下所示:

UIStream& operator<<(UIStream& os, std::ostream& (*pf)(std::ostream&));

(还有另外两个,将 std::ostream 替换为 std::basic_ios&lt;char&gt;std::ios_base,如果您想允许所有 操纵器),它们的实现将与以下之一非常相似 你的模板。事实上,如此相似,您可以使用您的模板 像这样的实现:

typedef std::ostream& (*ostream_manipulator)(std::ostream&);
UIStream& operator<<(UIStream& os, ostream_manipulator pf)
{
   return operator<< <ostream_manipulator> (os, pf);
}

最后一点,通常编写自定义 streambuf 通常是更好的方法 实现一个人试图实现的应用到你正在使用的技术。

【讨论】:

  • +1 这是我昨天提供的相同答案。不幸的是,它被忽略了。 stackoverflow.com/questions/1133739/…
  • 其实我找到了同样的解决方案,只是我使用了一个更简单的函数体:pf(*this); return *this;,但是我添加了op&lt;&lt;作为我派生的ostreamer类的成员。
【解决方案3】:

我这样做是为了解决我的问题,这是我的部分代码:

    template<typename T> 
    CFileLogger &operator <<(const T value)
    {
        (*this).logFile << value;
        return *this;
    }
    CFileLogger &operator <<(std::ostream& (*os)(std::ostream&))
    {
        (*this).logFile << os;
        return *this;
    }

Main.cpp

int main(){

    CFileLogger log();    
    log << "[WARNINGS] " << 10 << std::endl;
    log << "[ERRORS] " << 2 << std::endl;
    ...
}

我在这里得到了参考http://www.cplusplus.com/forum/general/49590/

希望这可以帮助某人。

【讨论】:

    【解决方案4】:

    请参阅here 了解扩展 IOStreams 的更好方法。 (有点过时了,而且是为 VC 6 量身定做的,所以你必须对它持保留态度)

    关键是要使函子工作(而 endl,它既输出“\n”,又是一个函子),您需要实现完整的 ostream 接口。

    【讨论】:

      【解决方案5】:

      std 流不是为子类而设计的,因为它们没有虚拟方法,所以我认为你不会走得太远。不过,您可以尝试聚合一个 std::ostream 来完成这项工作。

      要使endl 工作,您需要实现一个operator&lt;&lt; 版本,它需要一个指向函数的指针,因为这就是endl 等操纵器的处理方式,即

      UStream& operator<<( UStream&, UStream& (*f)( UStream& ) );
      

      UStream& UStream::operator<<( UStream& (*f)( UStream& ) );
      

      现在std::endl 是一个函数,它接受并返回对 std::basic_ostream 的引用,因此它不能直接与您的流一起使用,因此您需要制作自己的版本来调用 std::endl您聚合的std::iostream 中的版本。

      编辑:看起来 GMan 的答案更好。他也得到std::endl 的工作!

      【讨论】:

      • 我会支持这个答案:P
      • 其实不是。如果您愿意阅读我文章中的链接文章,您就会知道如何使所有仿函数工作,而不仅仅是您明确实现的那些。
      【解决方案6】:

      除了接受的答案之外,在 C++11 中,可以为类型重载 operator&lt;&lt;

      decltype(std::endl<char, std::char_traits<char>>)
      

      【讨论】:

        猜你喜欢
        • 2015-09-17
        • 2020-03-19
        • 2011-01-13
        • 2021-07-12
        • 2011-02-06
        • 1970-01-01
        • 1970-01-01
        • 2015-08-13
        相关资源
        最近更新 更多