当重载operator <<时,std :: endl是未知类型

Kaz*_*oom 58 c++ stl operator-overloading

我重载了运算符<<

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

UIStream my_stream;
my_stream << 10 << " heads";
Run Code Online (Sandbox Code Playgroud)

工作但是:

my_stream << endl;
Run Code Online (Sandbox Code Playgroud)

给出编译错误:

错误C2678:二进制'<<':找不到哪个运算符带有'UIStream'类型的左操作数(或者没有可接受的转换)

my_stream << endl工作的工作是什么?

GMa*_*ckG 81

std::endl是一个函数,std::cout通过实现operator<<获取具有相同签名的函数指针来利用它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;
}
Run Code Online (Sandbox Code Playgroud)

希望这能让您更好地了解这些事情是如何运作的.

  • 我没有downvote,但这里缺少一个重要的细节:std :: endl不是一个函数,而是一个模板化的函数.这意味着如果你试图定义一个通用操纵器接受运算符<< overload as:`template <typename T> mystream&operator <<(T&(*fp)(T&))`(这个签名将接受所有STL`basic_stream <> `,``ios_base`和`basic_ios <>`操纵符)编译器将无法将std :: endl与模板匹配,因为它本身就是一个模板,它无法定义T的含义. (5认同)
  • 请在投票时发表评论,以便我提高答案. (4认同)

APr*_*mer 36

问题是这std::endl是一个功能模板,就像你的运营商一样<< .所以当你写:

my_stream << endl;
Run Code Online (Sandbox Code Playgroud)

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

因此,您必须编写额外的非模板,操作符重载<<以使用操纵器.他们的原型看起来像:

UIStream& operator<<(UIStream& os, std::ostream& (*pf)(std::ostream&));
Run Code Online (Sandbox Code Playgroud)

(还有另外两个,替换std::ostreamstd::basic_ios<char>std::ios_base,如果你想允许所有的操纵器你也必须提供),它们的实现将与你的模板非常相似.实际上,如此相似,您可以使用您的模板进行实现,如下所示:

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

最后一点,通常是编写自定义streambuf通常是一种更好的方法来实现应用于您正在使用的技术.

  • +1这是我昨天提供的答案.不幸的是,它被忽略了.http://stackoverflow.com/questions/1133739/how-does-ofstream-or-ostream-type-cast-all-types-to-string/1134501#1134501 (2认同)

Luc*_*nde 7

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

    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;
    }
Run Code Online (Sandbox Code Playgroud)

Main.cpp的

int main(){

    CFileLogger log();    
    log << "[WARNINGS] " << 10 << std::endl;
    log << "[ERRORS] " << 2 << std::endl;
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

希望这可以帮助别人.


EFr*_*aim 5

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

要点是,要使函子工作(和 endl,它都输出“\n”并且刷新是一个函子),您需要实现完整的 ostream 接口。