C++20通过`operator>>`将阅读改为char数组-如何解决这个问题?

bq9c1y66  于 2023-06-25  发布在  其他
关注(0)|答案(1)|浏览(154)

编辑:总结评论(在我结束主题之前):
1.这个问题以前已经在这里讨论过:

决议是,委员会知道他们打破了代码与此。
1.我不知道是什么紧急的问题导致LWG替换旧版本(这是在C 98和C 17之间的标准。大约20年),而不是这样做的(因为它IMHO是在C14中使用std::gets完成的-有一个很好的理由):
第1步:只ADD新的模板化版本,它需要一个“数组引用”,在编译时从中推导出元素的数量(这肯定是对缓冲区溢出的保护)。它将从“第一天”起生效。
第2步:弃用从C 98到C17的标准版本,并等待社区反馈,如果有足够有效的用例来保留它。然后,可能会在一个标准之后删除它。
我认为一个有效的用例是我在这里展示的:https://godbolt.org/z/nG174vnqP
(从实际代码中提取,但缩短以仅显示问题)。包含也是一个小演示,为什么我认为旧版本和新版本可以很好地共存。但也许我的判断是错的。
我发现目前最烦人的是,如果不进入UB-land,就无法在C
20中解决这个问题。特别是当我认为“旧”版本仍然在内部可用,新版本只是转发给它-这是非常可能的,因为你不想为每个不同的数组长度单独实现。
随着C++20的发布,用于读取char数组的operator>>重载现在需要char(&)[N]参数,而不是char*。自C ++98以来正确编译的原始代码(看起来像下面的那样)将不再工作:

std::size_t sz = 10;
char *cp = new char[sz];
...
std::cin >> std::setw(sz) >> cp;

要纠正此错误,可以按如下方式修改代码:

std::cin >> std::setw(sz) >> *reinterpret_cast<char(*)[std::numeric_limits<int>::max()]>(cp));

标签:https://godbolt.org/z/svPcT4eao
此外,可变长度字符串的常见实现存在一个问题,即在编译时可以在没有指示的情况下悄悄更改。
为了回答评论中常见的问题,为什么我不使用std::string
事实上,我经常使用std::string,但我偶尔也会指导那些在不想增加任何不必要开销的项目中工作的人,有些人更喜欢不使用三个指针的字符串类,而一个指针就足够了。这个例子就是从其中的一个例子中提取出来的。
我也被指向这个Can't use std::cin with char* or char[] in C++20答案,是的,它是关于同一个主题,但这个答案中更重要的信息是它指向的LWG:https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0487r1.html
它清楚地表明,有效的C++17代码不再编译,但遗憾的是,它没有涵盖以前有效的代码不再有效的无声更改,但不会导致编译时错误:
至少考虑到

struct vbuf {
        std_::size_t sz;
        char cbuf[1];
    };

早期C++标准还没有将超分配技术转化为UB。
在下面的评论@n.m.他说这已经是C语言中的UB了。他可能是正确的(我没有检查所有的C标准,因为C89,我相对肯定,这不是UB然后),但至少这是一个常见的技术(例如。在Linux消息的缓冲区中,参见sndmsg(3P)等),因此我认为这是一个安全的假设-至少对于Linux系列的编译器-这是定义良好且安全的。
但我不会再声称新版本导致了“无声的变化”,因为这当然不适用于我们在UB土地上。

eqzww0vc

eqzww0vc1#

关于C20中使用运算符>>重载阅读char数组的更改的问题已在以下文档中讨论:https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0487r1.html。委员会达成的决议是他们意识到这些更改可能会破坏现有代码。
目前还不清楚为什么库工作组(LWG)决定替换在标准中存在了大约20年(C
98和C17之间)的旧版本的操作符>>,而不遵循不同的方法,就像C14中的std::gets一样。一种可能的办法是:
第1步:只添加新的模板化版本,它需要一个“数组引用”作为参数,在编译时从中推导出元素的数量。这种方法提供了防止缓冲区溢出的保护,并且从一开始就可以有效。
步骤2:弃用C98到C17标准中存在的操作符>>的版本,并收集社区反馈,以确定是否有有效的用例来保留它。然后,可能会在未来的标准中删除它。
可以在此处找到演示该问题的一个示例用例:https://godbolt.org/z/nG174vnqP。该示例摘自真实的代码,但已缩短以突出问题。此外,它还包括一个小演示,说明为什么旧版本和新版本可以共存。
目前的情况是令人沮丧的,因为在C20中没有直接的方法来解决这个问题,而不会进入未定义行为的领域。值得注意的是,operator>>的“旧”版本可能仍然在内部可用,新版本只是转发给它。该假设基于以下假设:针对不同阵列长度的单独实现将是不期望的。
随着C
20的发布,用于阅读char数组的操作符>>重载现在需要char(&)[N]参数而不是char*。为了解决这个问题,代码可以修改如下:

std::cin >> std::setw(sz) >> *reinterpret_cast<char(*)[std::numeric_limits<int>::max()]>(cp);
An example demonstrating this modification can be found here: https://godbolt.org/z/svPcT4eao.

还有一个与可变长度字符串的常见实现相关的问题,它可以在编译时不加指示地悄悄更改。
关于为什么不使用std::string的问题,重要的是要注意std::string被广泛使用,但在某些情况下,开发人员更喜欢不会引入不必要开销的字符串类。所提供的示例是从一个这样的案例中提取的。
在一条评论中,有人提到,在对“C20中不能将std::cin与char* 或char[]一起使用”(https://stackoverflow.com/questions/49918992/cant-use-stdcin-with-char-or-char-in-c20)问题的回答中也涉及了类似的主题。该答复中的重要信息包含在其中链接的LWG文件中:https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0487r1.html
值得注意的是,虽然这种使用结构vbuf进行过度分配的技术在早期的C
标准中可能被认为是安全的并且定义良好的,但现在它可能被认为是未定义的行为。

相关问题