如何初始化 std::stringstream

How to initialize a std::stringstream?

本文关键字:stringstream std 初始化      更新时间:2023-10-16

我需要用整数连接一个字符串。为此,我通过以下方式使用stringstream

int numPeople = 10;
stringstream ss;
ss << "Number of people is " << numPeople;

这奏效了。但我试图以以下方式做到这一点:

int numPeople = 10;
stringstream ss << "Number of people is " << numPeople;

我收到以下错误:">'<<'令牌之前的预期初始值设定项">

为什么我会收到此错误?为什么我不能在声明stringstream值的同时分配它?

stringstream ss << "Number of people is " << numPeople;

为什么我不能在声明stringstream值的同时分配它?

这类似于希望这会起作用...

int x + 3 + 9;

。但这并不能解析为变量定义,更不用说定义和赋值了。

定义和初始化对象的合法方式

使用非默认值定义初始化变量的唯一方法是(在语法意义上 - 这不是代码):

type identifier(...args...);
type identifier{...args...};
type identifier = ...expression...;

最后一个符号等效于第一个符号 - 即type identifier(arg)arg 传递...expression...的地方。

尝试对 int 和字符串流使用法律表示法

对于int,您可以轻松更正代码:

int x = 3 + 9;

。它之所以有效,是因为可以首先独立评估"3 + 9",以给出一个合理的值以存储在x中。编译器在int上对运算符+的行为做了我们想要的:它产生了我们想要存储在x中的int结果。 您可以将以上视为:

// evaluate the expression to assign first...
((int)3 + (int)9); // notate the implicit types
(int)12;           // evaluate expression => value to assign
int x = (int)12;           // then think about doing the assignment
int x((int)12);            // construct a valid value

它有效! 但是,如果您尝试stringstream

...
stringstream ss = "Number of people is " << numPeople;  // BROKEN
"Number of people is " << numPeople; // bitshift?!

。它不起作用,因为"Number of people is " << numPeople需要先进行评估,但这是非法的 - 你会收到如下错误:

error C2296: '<<' : illegal, left operand has type 'const char [20]'

问题是编译器仍在尝试应用按位移位操作,这只对数字有意义,因为我们要使用的<<重载要求任何"X <<Y"代码的左侧部分"X"是 - 或隐式转换为 -ostream&。 无法转换字符串文本。 此时,编译器不会注意到表达式结果将传递到的stringstream

字符串流解决方案

这是一个先有鸡还是先有蛋的问题,因为您需要在stringstream中组合您想要的右侧值来调用stringstream的构造函数,但为此您需要......stringstream. 您实际上可以通过临时stringstream来完成它:

static_cast<std::ostringstream&&>(std::ostringstream{} << "Number of people is " << numPeople)

不幸的是,需要强制转换,因为operator<<重载通过引用其ostream基类来处理stringstream,返回一个ostream&,因此您需要手动转换回stringstream类型,以便您可以调用std::stringstreammove 构造函数...

然后,完整的单线结构是...

std::ostringstream ss(static_cast<std::ostringstream&&>(std::ostringstream{} << "Number of people is " << numPeople));
...or...
auto&& ss = static_cast<std::ostringstream&&>(std::ostringstream{} << "Number of people is " << numPeople);

。但这太可怕了,无法想象。

使用宏使解决方案(可以说)不那么可怕

是的,你没看错。 根据您的感受,您可能会觉得宏有帮助或更糟......

#define OSS(VALUES) 
static_cast<std::ostringstream&&>(std::ostringstream{} << VALUES)
auto&& ss = OSS("Number of people is " << numPeople);

FWIW,您还可以使用宏来创建字符串...

auto&& s = OSS("Number of people is " << numPeople).str(); 

。或创建专用的宏...

#define STR(VALUES) 
static_cast<std::ostringstream&&>(std::ostringstream{} << VALUES).str()
auto&& s = STR("Number of people is " << numPeople);

(可以说)更好的做法 - 单独的构造和初始化

只需创建stringstream- 可以选择向构造函数提供单个string- 然后在第二个语句中使用operator<<

std::stringstream ss;
ss << "Number of people is " << numPeople;

这更容易阅读,并且不需要奇怪的宏。

另一种选择

C++11 引入了to_string()重载,如果您有一两个整数值来连接或放入string,则这些重载很方便:

auto&& s = "Number of people is " + std::to_string(numPeople);

不过,这可能效率低下(如果您关心,请检查您的编译器优化能力):每个std::to_string()都可能为独立的std::string实例动态分配缓冲区,然后单个串联可能涉及额外的文本复制,并且原始动态分配的缓冲区可能需要放大,然后大多数临时std::string在销毁过程中需要时间来释放。

讨论

理想情况下,std::stringstream会有一个构造函数接受任意数量的构造函数参数,这些参数(A, B, C...)格式化为stringstream,就像通过后续<< A << B << C...一样。 已经有带有参数的构造函数(例如(std::ios_base::openmode, const Allocator&)),因此我们需要一个占位符来区分此类参数与我们尝试格式化为流的值,或者更奇怪的解决方法,例如要求将要格式化的值作为初始化器列表传入流。

尽管如此,使用带有,而不是<<的字符串看起来和感觉都非常奇怪:

std::stringstream ss{"n:", std::setw(4), std::hex, 'n'};

然后,如果在代码维护期间发现需要在构造后将流值移动到某个点,则需要更改分隔符。 将其分成两行开始 - 施工然后流式传输 - 简化了维护。

03 C++年的情况更糟

C++03 缺少移动构造函数,因此有必要在临时上使用std::ostringstream::str()成员函数来获取用于构造命名stringsteamstd::string的额外深度副本......

stringstream ss(static_cast<std::ostringstream&>(std::ostringstream() << "Number of people is " << numPeople).str());

使用此 C++03 代码,可能会重复的动态内存分配(除非字符串足够短以适合字符串对象,这是一种通常提供的std::string技术,称为"短字符串优化"或 SSO)。 还有一个文本内容的深层副本。 先施工后流是一种更好的方法。

必须先初始化 stringbuf,然后才能向其添加值。 字符串流是一个对象。

通过做:

std::stringstream ss;

实质上是允许应用程序分配空间来处理要添加的值。