2

I ran into some trouble trying to inherit std::ostream and using a custom operator << which is basically doing some work and then forwarding to std::ostream <<, e.g.:

#include <iostream>
#include <ostream>
struct ostream : std::ostream{
    using std::ostream::ostream;
    template<typename T>
    ostream &operator <<(T &&arg){
        //some work...
        static_cast<std::ostream&>(*this) << std::forward<T>(arg);
        return *this;
    }
};

int main(){
    ostream cc(std::cout.rdbuf());
    cc << "hello world";
    //cc << "hello world" << std::endl; //couldn't deduce template parameter `T`
}

The problem is when using manipulators, like in the line I have commented out, gcc complains about [template argument deduction/substitution failed:].

Do I have to set the template type explicitly?, if so how?, because I cannot use the in class std::ostream::operator << due to incompleteness.

Live on Wandbox


Edit

I have just defined the custom operator << as a free function so not inside the class ostream

#include <iostream>
#include <ostream>
struct ostream : std::ostream{
    using std::ostream::ostream;
};
template<typename T>
ostream &operator <<(ostream &os, T &&arg)
{
    static_cast<std::ostream&>(os) << std::forward<T>(arg);
    return os;
}
int main(){
    ostream cc(std::cout.rdbuf());
    cc << "hello world" << std::endl;
}

and it is working as expected, also for manipulators. Not sure why this makes a difference here, maybe someone could clarify this for me

ezegoing
  • 526
  • 1
  • 4
  • 18
  • Related: https://stackoverflow.com/questions/4633864/how-do-the-stream-manipulators-work – πάντα ῥεῖ Mar 30 '19 at 09:20
  • Assuming, your actual intention is to add some additional formatting to a standard output stream: I once did it (successully) based on this: [SO: How to add indention to the stream operator](https://stackoverflow.com/a/9600752/7478597). (Btw. it doesn't require overloading of stream operators (`<<`).) – Scheff's Cat Mar 30 '19 at 09:50
  • @Scheff Thank you for your comment. It is going to be a circular_file struct `circular_ofstream` for server logs, where I am setting locks in `operator <<`. Therefore I stick to it. – ezegoing Mar 30 '19 at 09:57
  • Ah, OK. I must admit that your solution looks even more straight forward then the one I linked (although (for additional formatting) it's probably easier to manipulate the resulting character "stream" after stream operators have been processed.). – Scheff's Cat Mar 30 '19 at 09:59

1 Answers1

2

The problem is that the manipulators are templated, and your class does not provide the information needed for selecting the right template parameters for std::endl. You should overload the operator<< for the manipulators:

struct ostream : std::ostream{
    using std::ostream::ostream;

    template<typename T>
    ostream &operator <<(T &&arg){
        //some work...
        static_cast<std::ostream&>(*this) << std::forward<T>(arg);
        return *this;
    }
    ostream &operator<<(
      std::ostream &(*manip)(std::ostream&)) {
              //some work...
        static_cast<std::ostream&>(*this) <<manip;
        return *this;
    }
};

Note that the code in the question failed for the same reason that the following fails:

auto manip = std::endl;

It simply can't deduce the template parameters of endl.

Update

The alternative that makes the ovloading as a free function does not do what one may expect:

template<typename T>
ostream &operator <<(ostream &os, T &&arg)
{
    static_cast<std::ostream&>(os) << std::forward<T>(arg);
    return os;
}
int main(){
    ostream cc(std::cout.rdbuf());
    cc << "hello world" << std::endl;
}

The operator that gets std::endl is from the original iostream library. The compiler does not execute the overloaded function in this case, and that's why there is no compilation error.

If you don't want to overload the manipulators, you can provide the template parameters explicitly:

cc << "hello world" << std::endl<char, std::char_traits<char>>;
Michael Veksler
  • 8,217
  • 1
  • 20
  • 33
  • Hi Thank you for your answer. I actually wanted to avoid to overload for manipulators. Anyway I found out that overloading can be avoided if the `operator <<` is defined as a free function (see edit) . Why can it deduce the template parameter when defined as free function but not in class? – ezegoing Mar 30 '19 at 10:25
  • I dont really understand why for the free function itself (which is template too), I mean there is a `T` which has to be deduced too, there is no deduction failure then. I dont see the difference to defining it inside the class or outside for type deduction, but I am probably just wrong. – ezegoing Mar 30 '19 at 10:55
  • @czorp because the wrong free function is called, not yours. The wrong function overloads the manipulators – Michael Veksler Mar 30 '19 at 11:01
  • Ohhhhh ofc. omg I am so stupid XD. Thank you very much!!! Thank you for your time – ezegoing Mar 30 '19 at 11:02