流操纵器的免费函数与直接重载运算符<<

Max*_*hof 6 c++ operator-overloading iomanip

我正在研究自定义流类的可能设计。在场内和场外搜索了半个小时都没有得到我的问题的答案,所以我提出了一个新问题。

std::endl作为一个例子。如果我没有记错的话,它被指定为大致像这样工作(认为这是“幻灯片代码”):

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

ostream& endl(ostream& s)
{
    /* ...do whatever... */
    return s;
}
Run Code Online (Sandbox Code Playgroud)

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

但是,我不清楚这样做的好处是什么,而不是拥有标记对象并operator<<直接为它们实现:

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

https://godbolt.org/z/dzj7v8fse

我知道第一个版本为额外的操纵器编写的代码略少。这不是最糟糕的原因,但它似乎更令人费解(有多少人知道这std::endl实际上是一个函数),并且偏离了具有参数的操纵器的实现(例如,std::setw(n)返回一个存储的对象n并为其提供适当的operator<<重载存在 - 非常接近第二个片段)。

我想知道这里是否还有其他考虑因素(在标准化时或在查看当前 C++ 版本时)。也许重载决议的微妙之处?编译速度?易于优化?模板友好?错误信息?或者我在新代码中选择哪种方法根本无关紧要?