使用rdbuf()->in_avail()检测stringstream是否合规?C++17/20行为有变化吗?
std::stringstream中rdbuf()->in_avail()用法的定义性与C++版本差异
问题背景
代码通过std::stringstream收集输出内容,随后调用rdbuf()->in_avail()判断流中是否有内容,若存在则写入std::cout。但不同编译器在C17和C20模式下表现不一致:GCC-13两种模式均输出内容,MSVC 17.6仅在C17模式下输出,C20模式下无输出。需明确该用法是否定义良好,以及C17到C20的行为变化。
示例代码:
#include <sstream> #include <iostream> int main() { std::stringstream ss; ss.str(""); ss << "kalle tar kulan"; if (ss.rdbuf()->in_avail()) std::cout << ss.rdbuf() << "\n"; }
解答
1. 原用法并非定义良好
rdbuf()->in_avail()返回输入序列中当前可读取的字符数(即egptr() - gptr()),但该值的有效性依赖于stringstream输入指针的位置。在执行输出操作后,输入指针的位置在C++17及之前的标准中未被明确规定,属于实现定义行为,因此原代码的逻辑不可靠。
2. C17与C20的行为差异
- C++17及之前:多数编译器(如GCC、旧版MSVC)在执行输出操作后,会让
stringstream的输入指针停留在缓冲区起始位置,此时in_avail()返回缓冲区总字符数,条件成立,输出内容。但这是实现层面的选择,并非标准强制要求。 - C++20:标准明确调整了
std::basic_stringbuf的行为:完成输出操作后,输入序列的指针会被设置为与结束指针重合(gptr() == egptr()),此时in_avail()返回0,条件不成立,不会输出内容。这一调整明确了双向流的模式切换规则——从输出模式切换到输入模式时,必须显式重置输入指针位置。
3. 不同编译器表现差异原因
- GCC-13在C20模式下仍输出内容,是因为其实现暂时保留了旧行为,未严格遵循C20的新规则;
- MSVC 17.6严格执行C20标准,因此出现了与C17模式不同的结果。
4. 正确的替代写法
要保证跨版本、跨编译器的一致性,需在判断前显式重置输入指针到起始位置:
#include <sstream> #include <iostream> int main() { std::stringstream ss; ss.str(""); ss << "kalle tar kulan"; ss.seekg(0); // 显式将输入指针移到缓冲区开头 if (ss.rdbuf()->in_avail()) std::cout << ss.rdbuf() << "\n"; }
或者更直观地直接检查字符串是否为空(效率略低,但代码更简洁):
#include <sstream> #include <iostream> int main() { std::stringstream ss; ss.str(""); ss << "kalle tar kulan"; if (!ss.str().empty()) { ss.seekg(0); std::cout << ss.rdbuf() << "\n"; } }
内容的提问来源于stack exchange,提问作者Johan
相关产品推荐
相关产品推荐

