考虑以下功能:
void f(const char* str);
假设我想使用stringstream生成一个字符串并将其传递给此函数。如果我想在一个声明中这样做,我可能会尝试:
f((std::ostringstream() << "Value: " << 5).str().c_str()); // error
这给出了一个错误:'str()'不是'basic_ostream'的成员。好的,所以运营商&lt;&lt;正在返回ostream而不是ostringstream - 如何将它转换回ostringstream?
1)这个演员安全吗?
f(static_cast<std::ostringstream&>(std::ostringstream() << "Value: " << 5).str().c_str()); // incorrect output
现在有了这个,结果是运算符&lt;&lt;(Value:“)调用,它实际上调用了ostream的运算符&lt;&lt;(void *)并打印了一个十六进制地址。这是错的,我想要文本。
2)为什么运营商&lt;&lt;在临时std :: ostringstream()上调用ostream运算符?当然临时有一种'ostringstream'而不是'ostream'?
我可以施放临时来强制执行正确的操作员调用!
f(static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << "Value: " << 5).str().c_str());
这似乎可以正常工作并将“Value:5”传递给f()。
3)我现在依赖于未定义的行为吗?演员阵容看起来很不寻常。
我知道最好的选择是这样的:
std::ostringstream ss;
ss << "Value: " << 5;
f(ss.str().c_str());
...但我对在一行中做这件事的行为很感兴趣。假设有人想制作一个(可疑的)宏:
#define make_temporary_cstr(x) (static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << x).str().c_str())
// ...
f(make_temporary_cstr("Value: " << 5));
这会按预期运作吗?
答案 0 :(得分:14)
您无法将临时流转发给std::ostringstream&
。它是不正确的(编译器必须告诉你它是错误的)。但是,以下情况可以做到:
f(static_cast<std::ostringstream&>(
std::ostringstream().seekp(0) << "Value: " << 5).str().c_str());
那当然很难看。但它显示了它是如何工作的。 seekp
是返回std::ostream&
的成员函数。通常可能更好地写这个
template<typename T>
struct lval { T t; T &getlval() { return t; } };
f(static_cast<std::ostringstream&>(
lval<std::ostringstream>().getlval() << "Value: " << 5).str().c_str());
void*
之所以没有任何内容,是因为operator<<
是一个成员函数。需要operator<<
的{{1}}不是。
答案 1 :(得分:3)
临时不能作为非const引用传递给函数,这就是为什么它找不到正确的流操作符而是采用带有void *参数的那个(它是一个成员函数,因此临时调用它)没关系。)
通过施法绕过限制是什么,我感觉它实际上是UB,但我不能肯定地说。其他人肯定会引用标准。
答案 2 :(得分:1)
这可以使用C ++ 11 lambda函数完成。
secs
答案 3 :(得分:0)
如果你喜欢one_lined语句,你可以写:
// void f(const char* str);
f(static_cast<ostringstream*>(&(ostringstream() << "Value: " << 5))->str());
但是,您应该更喜欢将代码维护为:
template <typename V>
string NumberValue(V val)
{
ostringstream ss;
ss << "Value: " << val;
return ss.str();
}
f(NumberValue(5));
答案 4 :(得分:0)
我使用类似的东西进行日志记录。
#include <sstream>
using namespace std;
const char *log_text(ostringstream &os, ostream &theSame)
{
static string ret; // Static so it persists after the call
ret = os.str();
os.str(""); // Truncate so I can re-use the stream
return ret.c_str();
}
int main(int argc, char **argv)
{
ostringstream ss;
cout << log_text(ss, ss << "My first message") << endl;
cout << log_text(ss, ss << "Another message") << endl;
}
输出:
我的第一条消息
另一条消息
答案 5 :(得分:0)
自C ++ 17以来,我们为模板提供了折叠表达式,可用于制作通用字符串格式化程序,以接受具有流支持的所有内容:
template <class... Args>
std::string format(Args &&... args)
{
std::ostringstream ostr;
(ostr << ... << args);
return ostr.str();
}
您可以像format("Int number: ", 42, " Float number: ", 3.14).c_str()
那样使用它。
在C ++ 20中,std::format即将到来。