std::endl is of unknown type when overloading operator<<
Asked Answered
O

6

69

I overloaded operator <<

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

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

Works but:

my_stream << endl;

Gives compilation error:

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

What is the work around for making my_stream << endl work?

Odle answered 15/7, 2009 at 22:6 Comment(2)
Without knowing anything about UIStream, it's a bit difficult to comment.Demonstrator
And yet, you found a way. :)Henryson
L
94

std::endl is a function and std::cout utilizes it by implementing operator<< to take a function pointer with the same signature as std::endl.

In there, it calls the function, and forwards the return value.

Here is a code example:

#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;
}

Hopefully this gives you a better idea of how these things work.

Leilaleilah answered 15/7, 2009 at 22:27 Comment(4)
Please leave a comment when you down vote so I can improve my answer.Leilaleilah
I did not downvote, but there is an important detail missing here: std::endl is not a function but a templated function. This means that if you try to define a generic manipulator accepting operator<< overload as: template <typename T> mystream& operator<<( T& (*fp)(T&) ) (this signature would accept all STL basic_stream<>, ios_base and basic_ios<> manipulators) the compiler will not be able to match std::endl against the template, as it is a template in itself and it cannot define what T means.Melanosis
Thanks! This helped me in answering another question. stackoverflow.com/questions/2196155Coburn
Why the typedef CoutType instead of just using ostream?Glycine
B
38

The problem is that std::endl is a function template, as your operator << is. So when you write:

my_stream << endl;

you'll like the compiler to deduce the template parameters for the operator as well as for endl. This isn't possible.

So you have to write additional, non template, overloads of operator << to work with manipulators. Their prototype will look like:

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

(there are two others, replacing std::ostream by std::basic_ios<char> and std::ios_base, which you have also to provide if you want to allow all manipulators) and their implementation will be very similar to the one of your templates. In fact, so similar that you can use your template for implementation like this:

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

A final note, often writing a custom streambuf is often a better way to achieve what one try to achieve applying to technique you are using.

Bombe answered 16/7, 2009 at 10:7 Comment(2)
+1 this is the same answer I provided yesterday. Unluckily it has been ignored. stackoverflow.com/questions/1133739/…Melanosis
Actually I found the same solution, but I used a simpler function body: pf(*this); return *this;, but I add op<< as a member of my derived ostreamer class.Unlearned
S
10

I did this to solve my problem, here is part of my code:

    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;
    ...
}

I got the reference in here http://www.cplusplus.com/forum/general/49590/

Hope this can help someone.

Sosthina answered 9/1, 2016 at 20:7 Comment(0)
V
5

See here for better ways of extending IOStreams. (A bit outdated, and tailored for VC 6, so you will have to take it with a grain of salt)

The point is that to make functors work (and endl, which both outputs "\n" and flushes is a functor) you need to implement the full ostream interface.

Vanir answered 15/7, 2009 at 22:11 Comment(0)
L
4

The std streams are not designed to be subclassed as they have no virtual methods so I don't think you'll get too far with that. You can try aggregating a std::ostream to do the work though.

To make endl work you need to implement a version of operator<< that takes a pointer-to-function as that is how the manipulators such as endl are handled i.e.

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

or

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

Now std::endl is a function that takes and returns a reference to a std::basic_ostream so that won't work directly with your stream so you'll need to make your own version which calls through to the std::endl version in your aggregated std::iostream.

Edit: Looks likes GMan's answer is better. He gets std::endl working too!

Lollard answered 15/7, 2009 at 22:28 Comment(2)
I will support this answer :PLeilaleilah
Actually not. If you'd care to read the linked article from my article, you'd know how to make ALL functors work, not just those you explicitly implemented.Vanir
F
1

In addition to the accepted answer, with C++11 it is possible to overload operator<< for the type:

decltype(std::endl<char, std::char_traits<char>>)
Franciscofranciska answered 11/1, 2016 at 11:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.