【问题标题】:"Overriding" ostream& operator <<“覆盖” ostream& 运算符 <<
【发布时间】:2014-01-05 00:06:48
【问题描述】:

我想实现一个字符串流对象,它代表某个类型的字符串流。

例如,

class ohtmlstringstream : public std::ostringstream {
};
ohtmlstringstream& operator <<(ohtmlstringstream& ohss, double d) {
    ohss << "<div title='double'>" << d << "</div>";
    return ohss;
}

遗憾的是,这不起作用,并且我创建的任何 ohtmlstringstream 对象,我将 &lt;&lt; 加倍放入只是运行 std::ostream&amp; operator &lt;&lt;(std::ostream&amp;, double)

这样做的方法是什么?看起来我不应该继承 ostringstream。

我真的只是想要一种干净简单的方法来“分叉”特定类型的序列化方式。很长一段时间以来,我只想以一种方式序列化一个类型,但现在我想为我的类型构建至少 3 种情况的字符串表示:在 HTML(或 XML)表示中,在 JSON 中,在我已经这样做了,这是调试时转储到终端的基本方法。

【问题讨论】:

  • 我很确定你不能从标准类继承,除了异常类。
  • @user2345215 是的,那我该怎么办?我应该做一些stringbuf inheriting 吗?或者我可以让我的类包含它自己的字符串流吗?
  • Works for me。问题一定出在你没有显示的代码上。
  • @IgorTandetnik 但这不是:ideone.com/iXnRmM 问题是它不像我预期的那样工作
  • 当然。这是operator&lt;&lt;(operator&lt;&lt;(ostream, const char*), double)。内部调用返回ostream&amp;,而不是ohtmlstringstream&amp;

标签: c++ stream iostream


【解决方案1】:

抛开从std::ostringstream 派生的一般智慧,也许是一个全球性的

template<typename T> 
ohtmlstringstream & operator <<(ohtmlstringstream& ohss, T const & t)

以下方式会为你做的:

#include <sstream>
#include <iostream>
#include <typeinfo>

using namespace std;

class ohtmlstringstream : public ostringstream {};

template<typename T>
ohtmlstringstream & operator <<(ohtmlstringstream& ohss, T const & t)
{
    static_cast<ostringstream &>(ohss) 
        << "<div title='"
        << typeid(T).name() // For the sake of illustration
        << "'>"
        << t << 
        "</div>\n";
    return ohss;
}

// Testing ...
int main()
{
    ohtmlstringstream ohtml;
    ohtml << string("Testing") 
        << '1' << 2 << 3.0f << "4" << endl << "Bye" << endl;
    cout << ohtml.str() << endl;
    return 0;
}

输出:

<div title='Ss'>Testing</div>
<div title='c'>1</div>
<div title='i'>2</div>
<div title='f'>3</div>
<div title='A2_c'>4</div>

Bye

typeid(T).name() 无疑不是T 的令人满意的描述。你 可能会用专业化的习惯来代替它,比如:

template<typename T>
std::string type_desc(T const &)
{
    return typeid(T).name();
}

对于您需要的所有T(让自己接受这么多的苦差事)。

请注意,插入操纵器(例如std::endl)会终止“html-ization”, 因为插入会返回对基址 std::ostream 的引用。

如果您不希望这种情况发生,您可能需要操纵器 仅在 html 输出中注明,但不采取行动。所以你可能 添加专业化:

inline ohtmlstringstream & 
operator <<(ohtmlstringstream& ohss, ostream & (*pf)(ostream &))
{
    static_cast<ostringstream &>(ohss) 
        << "<div title='"
        << typeid(pf).name()
        << "'>"
        "</div>\n";
    return ohss;
}

加上这个,输出变成:

<div title='Ss'>Testing</div>
<div title='c'>1</div>
<div title='i'>2</div>
<div title='f'>3</div>
<div title='A2_c'>4</div>
<div title='PFRSoS_E'></div>
<div title='A4_c'>Bye</div>
<div title='PFRSoS_E'></div>

(使用 gcc 4.8.2 和 clang 3.3 构建)

【讨论】:

  • 啊哈!在 ohtmlstringstream 的模板化 operator&lt;&lt; 中使用 static_cast 很聪明,我想知道我需要多长时间才能想出它。
  • 只是为了留下更多的面包屑......我实际上并没有最终使用继承来实现这一点,正如建议的那样:我想我可能想在我的 html 序列化程序对象中跟踪更多状态。然而,这仍然是一个很好的答案!
【解决方案2】:

如果您只对数字类型感兴趣,您可以使用自定义std::num_put&lt;...&gt; 构面来格式化值。但是,其他类型一般不会让你拦截处理。

【讨论】:

  • 是的,不,我绝对有兴趣在所有类型上执行此操作。
  • 感谢您提出这个问题,Igor 也提到了这个。它促使我在gcc -E 输出中四处寻找,我发现基本类型的operator &lt;&lt; 模板实现充满了错误检查和应用num_put 以及其他似乎不太必要的东西。看起来我可以编写一些我自己的模板来构建一个更快的“字符串流”,实际上我可能必须实现它们才能获得像ohtmlstringstream 这样的自定义流类