在单个语句中将临时字符串流转换为c_str()

时间:2010-03-12 13:44:14

标签: c++ string stringstream temporary

考虑以下功能:

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));

这会按预期运作吗?

6 个答案:

答案 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即将到来。