【发布时间】:2017-05-06 02:26:07
【问题描述】:
在某些语言(如 Ruby)中,有用于创建格式化输出的精美语法 作为“单线”,部分原因是 Ruby 应用程序需要这种方式的普遍性。
在一些 C++ 应用程序中也经常需要它。下面是一些示例代码:
if (num_doggies > num_biscuits) {
std::ostringstream ss;
ss << "There are " << num_doggies" << " puppy dogs and only " << num_biscuits << " biscuits!";
log_warn(ss.str());
this->negotiate_biscuit_reduction();
}
一些开源 C++98 项目的解决方法如下所示:
struct string_formatter {
std::ostringstream ss;
template <typename T>
string_formatter & operator << (const T & t) {
ss << t;
return *this;
}
operator std::string() const {
return ss.str();
}
};
这样他们就可以写出这样的东西:
if (num_doggies > num_biscuits) {
log_warn(string_formatter{} << "There are " << num_doggies" << " puppy dogs and only " << num_biscuits << " biscuits!");
this->negotiate_biscuit_reduction();
}
将三行变成一行。
但是,从代码审查的角度来看,string_formatter 类看起来相当邪恶,
主要是因为这种隐式转换。通常考虑隐式转换
非常邪恶,当它们转换为普通或原始类型时,它们更加邪恶
喜欢int 或std::string。
在 C++11 中我们可以稍微改变一下:
operator std::string() && {
return ss.str();
}
现在只有当string_formatter 是一个右值引用时才会触发转换。
所以它不太可能触发,但它也更复杂。它实际上是不那么邪恶吗?
值得商榷。
另外值得一提的是:其他人已经进行了更复杂的黑客攻击以使其正常工作
没有string_formatter 垫片:https://codereview.stackexchange.com/questions/6094/a-version-of-operator-that-returns-ostringstream-instead-of-ostream
这里没有隐式转换,但我们在标准库类型上重载运算符,这通常是禁忌。
让我们稍微备份一下。为什么我不能妥协并做到这一点?它至少更紧凑一点:
if (num_doggies > num_biscuits) {
std::ostringstream ss;
log_warn((ss << "There are " << num_doggies" << " puppy dogs and only " << num_biscuits << " biscuits!").str());
this->negotiate_biscuit_reduction();
}
事实证明这不会编译,因为std::ostringstream::operator << 返回std::ostream & 而不是
std::ostringstream &。当所有<< 都解决时,我们只有一个ostream &,所以我们不能使用.str()。
我的问题是,为什么会这样?
为什么std::ostringstream::operator << 不返回std::ostringstream &?是吗:
- 出于技术或安全原因不受欢迎
- 为标准库实现者增加不必要的困难
- 旧版 - 一直都是这样,现在更改它可能会破坏代码。 (但是,这究竟会破坏什么代码?由于继承关系,
std::ostringstream &可以通过标准转换转换为std::ostream &?) - 默默无闻——没人关心!
注意:我很清楚设计string_formatter 和记录器类的不同方法。我特别感兴趣的是标准库中是否存在使std::ostringstream 返回std::ostream & 更好的设计原因。
【问题讨论】:
-
你可以简单地将
.str()添加到string_formatter,不是吗? -
@T.C.是的,但这不是重点,如果有什么我不明白为什么
std::ostringstream需要返回std::ostream &的充分理由,我会更感兴趣 -
因为ostringstream中没有operator
-
@ChrisBeck - 当然,可以为 every ostream 类型的 every 插入器提供包装函数。这也意味着用户编写的流必须为每个插入器提供包装器,并且用户编写的流插入器必须为每个 ostream 类型提供包装器。就个人而言,我宁愿只写一个可以在任何地方工作的插入器,而不是 N 个适用于所有风格的 ostream 的插入器,除了我忘记的那些。
-
我想有人可以说
ostream&-taking,ostream&-returningoperator<<s 是自定义点,然后在顶部为具体流提供全局适配器,类似于operator<<的工作方式对于右值流。这样的设计具有显着的额外复杂性,并且似乎不是很有用。(os << a << b).stuff()既不漂亮也不优雅;如果由于某种原因您必须将其压缩到一行中,带有逗号运算符的os << a << b, os.stuff()可以正常工作,并且几乎一样丑陋。
标签: c++ c++11 stream c++-standard-library c++98