15

考虑以下函数:

void f(const char* str);

假设我想使用 stringstream 生成一个字符串并将其传递给这个函数。如果我想在一个声明中做到这一点,我可以尝试:

f((std::ostringstream() << "Value: " << 5).str().c_str()); // error

这给出了一个错误:“str()”不是“basic_ostream”的成员。好的,所以 operator<< 正在返回 ostream 而不是 ostringstream - 将其转换回 ostringstream 怎么样?

1)这个演员表安全吗?

f(static_cast<std::ostringstream&>(std::ostringstream() << "Value: " << 5).str().c_str()); // incorrect output

现在有了这个,对于 operator<<("Value: ") 调用,它实际上是在调用 ostream 的 operator<<(void*) 并打印一个十六进制地址。这是错误的,我想要文本。

2) 为什么临时 std::ostringstream() 上的 operator<< 调用 ostream 运算符?当然临时有一种'ostringstream'而不是'ostream'?

我也可以强制执行正确的操作员呼叫!

f(static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << "Value: " << 5).str().c_str());

这似乎有效并将“值: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));

这个功能会如预期的那样吗?

4

6 回答 6

16

您不能将临时流转换为std::ostringstream&. 它格式错误(编译器必须告诉您它是错误的)。但是,以下可以做到这一点:

f(static_cast<std::ostringstream&>(
  std::ostringstream().seekp(0) << "Value: " << 5).str().c_str());

这当然是丑陋的。但它显示了它是如何工作的。seekp是返回 a 的成员函数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<<不是char const*

于 2010-03-12T13:55:13.440 回答
3

临时不能作为对函数的非常量引用传递,这就是为什么它找不到正确的流式操作符,而是使用带有 void* 参数的操作符(它是一个成员函数,因此在临时调用它是可以的) .

通过铸造来绕过限制,我感觉它实际上是 UB,但我不能肯定地说。其他人肯定会引用该标准。

于 2010-03-12T13:47:12.093 回答
1

这可以使用 C++11 lambda 函数来完成。

#include <iostream>
#include <sstream>

void f(const char * str)
{
    std::cout << str << std::endl;
}

std::string str(void (*populate)(std::ostream &))
{
    std::ostringstream stream;
    populate(stream);
    return stream.str();
}

int main(int argc, char * * args)
{
    f(str([](std::ostream & ss){ ss << "Value: " << 5;  }).c_str());
    return 0;
}

// g++ -std=c++11 main.cpp -o main
// ./main
// Value: 5
于 2017-11-04T00:28:51.690 回答
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));
于 2010-03-12T15:36:14.463 回答
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;
}

输出:

我的第一条消息

另一条消息

于 2012-05-08T10:30:42.347 回答
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即将到来。

于 2020-02-21T18:05:18.687 回答