关于c ++:std :: endl在重载operator<<

std::endl is of unknown type when overloading operator<<

我操作人员超载<<

1
2
3
4
5
template <Typename T>
UIStream& operator<<(const T);

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

但工作:

1
my_stream << endl;

给出编译错误:

error C2678: binary '<<' : no operator found which takes a left-hand operand of type 'UIStream' (or there is no acceptable conversion)

使my_stream << endl工作的方法是什么?


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

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

下面是一个代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#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是一个函数模板,就像您的操作符<<一样。是。所以当你写:

1
my_stream << endl;

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

因此,您必须编写额外的、非模板的、运算符<<重载到使用操纵器。他们的原型将是:

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

(还有两个,用std::basic_ios替换std::ostreamstd::ios_base,如果你想让所有的和它们的实现将非常类似于你的模板。事实上,与使用模板这样的实现:

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

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


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

1
2
3
4
5
6
7
8
9
10
11
    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;
    }

主CPP

1
2
3
4
5
6
7
int main(){

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

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

希望这能帮助别人。


有关扩展iostream的更好方法,请参阅此处。(有点过时了,而且是为VC 6量身定做的,所以你得带上一粒盐)

要点是,要使函数正常工作(endl,它同时输出""和flushes是一个函数),需要实现完整的ostream接口。


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

要使endl工作,您需要实现一个operator<<的版本,该版本接受一个指向函数的指针,这就是如何处理诸如endl之类的操纵器,即

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

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

现在,std::endl是一个函数,它接受并返回一个对std::basic的引用,这样就不会直接与流一起工作,所以您需要自己的版本,它调用聚合std::iostream中的std::endl版本。

编辑:看起来像GMAN的答案更好。他也让std::endl工作!


除了接受的答案之外,用C++ 11可以为类型过载EDOCX1×2Ω。

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