C++ istream读入char*在C++20下触发C2679编译报错问题
is >> buffer写法的说明 在使用C++20标准编译书中这段代码时,会抛出如下编译错误:
C2679 binary '>>': no operator found which takes a right-hand operand of type 'char *' (or there is no acceptable conversion)
代码在旧标准下的合法性与运行逻辑
这段代码不是作者笔误,在C20标准发布前,代码完全符合C标准规范。
C++17及更早版本的标准库为std::istream的流提取运算符>>提供了接收char*类型右操作数的重载,配合前置的is.width(max)调用,运行逻辑和书中注释完全一致:
- 自动跳过输入流起始位置的所有空白字符(空格、制表符、换行符等)
- 从第一个非空白字符开始逐字符读取,直到遇到下一个空白字符停止
- 受
width(max)设置约束,最多读取max-1个字符,预留1字节位置用于自动追加C风格字符串结束符\0 - 读取完成后自动在缓冲区末尾写入
\0,保证缓冲区内容是合法的以空字符结尾的字符串 - 最终返回流对象本身,支持链式调用
这也解释了为什么在C14、C17标准下测试时,代码可以正常编译运行。
C++20下编译报错的原因
C20标准正式移除了operator>>对char*类型的支持,这个改动是为了从标准层面规避缓冲区溢出风险:
接收裸char*的重载无法在编译期感知缓冲区的实际大小,完全依赖开发者手动调用width()设置读取上限,一旦开发者遗漏这一步,输入长度超过缓冲区大小时就会发生越界写,是非常常见的内存安全漏洞来源。
C20保留了对固定长度字符数组char[N]的流提取支持,编译器可以在编译期获取数组长度自动做边界校验,安全性远高于裸指针传参,因此直接删除了风险更高的char*重载,这就是C++20环境下(无论VS2022、GCC还是Clang)出现编译错误的根本原因。
适配C++20的修改方式
如果需要在C++20及后续标准中实现书中代码的功能,推荐两种安全的改法:
- 改法1:使用模板接收固定长度字符数组,自动推导缓冲区长度,保留原代码C风格缓冲区的逻辑
template<std::size_t N> istream& read_word(istream& is, char (&buffer)[N]) { is.width(N); // 自动使用数组长度作为读取上限,不需要额外传max参数 is >> buffer; return is; }
- 改法2:使用
std::string接收输入,完全不需要手动管理缓冲区大小,是现代C++的推荐写法
istream& read_word(istream& is, std::string& str) { is >> str; return is; }
如果只是为了跑通书中的示例代码,直接将编译标准切换到C++17也可以正常运行,但不建议在新写的代码中继续使用裸char*做流输入参数。
内容的提问来源于stack exchange,提问作者CPPL

