Free functions for stream manipulators vs overloading operator<< directly

134 Views Asked by At

I'm looking at possible designs for a custom stream class. Half an hour of searching on- and off-site have not led to an answer to my queries, so I'm asking a new question instead.

Take std::endl as an example. It is, if I am not mistaken, specified to work roughly like this (consider this to be "slide code"):

struct ostream
{
    using FManip = ostream& (*)(ostream&);
    ostream& operator<<(FManip f)
    {
        return f(*this);
    }
};

ostream& endl(ostream& s)
{
    /* ...do whatever... */
    return s;
}

https://godbolt.org/z/6MP5cseas

However, it is unclear to me what the benefit of doing that is, as opposed to having tag objects and implementing operator<< directly for them:

struct ostream
{
};

// (reserved name - use whatever convention fits when writing library code)
struct _EndlManipTag {};
static constexpr _EndlManipTag endl;

ostream& operator<<(ostream& s, _EndlManipTag tag)
{
    /* ...do whatever... */
    return s;
}

int main()
{
  ostream os;
  os << endl;
}

https://godbolt.org/z/dzj7v8fse

I understand that the first version is slightly less code to write for additional manipulators. That's not the worst reason, but it also seems more convoluted (how many people know that std::endl is actually a function), as well as straying further from the implementation of manipulators that have arguments (e.g. std::setw(n) returns an object that stores n and for which an appropriate operator<< overload exists - very close to the second snippet).

I wonder if there are any other considerations at play here (either at time of standardization or when looking at current C++ versions). Maybe subtleties in overload resolution? Compilation speed? Ease of optimization? Template friendliness? Error messages? Or is it simply of no relevance which approach I choose in new code?

0

There are 0 best solutions below