与 std::string 一起使用的 std::replace_copy 的奇怪行为

Posted

技术标签:

【中文标题】与 std::string 一起使用的 std::replace_copy 的奇怪行为【英文标题】:Weird behavior of std::replace_copy used with std::string 【发布时间】:2013-12-20 14:52:28 【问题描述】:

我有这个简单的代码:

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main()

    string s = "1,0";
    string result;

    //result.resize(s.length());
    replace_copy(s.begin(), s.end(), result.begin(), ',', '.');

    cout << '"' << result         << '"' << endl;
    cout << '"' << result.c_str() << '"' << endl;
    cout << result.length() << endl;

    return 0;

result.resize 行未注释的此程序的控制台输出是:

“1.0” “1.0” 3

- 好的,但是当result.resize 的行被注释掉时,输出是:

“” “1.0” 0

-这会导致奇怪的错误,因为result != result.c_str() !!!

replace_copy(可能还有类似的模板)的这种行为是否可以被视为标准库中的错误?我找不到与该主题相关的任何内容。谢谢。

编译器:mingw32-g++ 4.7.1

【问题讨论】:

【参考方案1】:

你期待什么?

没有resize,您的字符串中就没有新字符的空格。

无论如何尝试复制到该空间肯定会导致“奇怪”[阅读:undefined] 行为。你正在破坏你的记忆。

replace_copy 复制到目标范围,这与将新元素插入目标容器不同。该范围必须已经存在...

...除非您使用back_inserter,它作为一种假范围,实际上在引擎盖下执行插入:

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main()

    string s = "1,0";
    string result;

    //result.resize(s.length());   // Look, ma! No hands!
    replace_copy(
        s.begin(), s.end(),
        std::back_inserter<std::string>(result),
        ',', '.'
    );

    cout << '"' << result         << '"' << endl;
    cout << '"' << result.c_str() << '"' << endl;
    cout << result.length() << endl;


// "1.0"
// "1.0"
// 3

Live demo

警告!在现场演示中获得正确的输出并不能证明任何事情,因为未定义的行为有时会“看似有效”。但是,我有 96k 代表,你可以相信我。 ;)

【讨论】:

+1 用于提及back_inserter。这是我首先想到的事情之一。 关于我对 STL 是精心设计的库的期望:为什么不 replace_copy 在这种情况下简单地不让目标保持不变(或者至少生成断言),而是违反字符串内部的不变量?为此,我问是否可以认为是标准库中的错误。 @user3123061:这不是标准库的错误,而是您使用它的错误。 由您维护不变量,而不是强制执行并非每个人都想要的可能昂贵的运行时检查。更实际地,从单个迭代器中检测范围的 lengthvalidity 是不可行的。【参考方案2】:

当你使用语句时

result.resize(s.length());

您创建并初始化(更精确地分配)了包含三个值为“\0”的元素的字符串。不使用此语句时,字符串没有元素,程序的行为未定义。实际上,未注释行的代码等效于以下代码:

string s = "1,0";
string result( s.length(), '\0' );

replace_copy(s.begin(), s.end(), result.begin(), ',', '.');

如果要像注释语句那样写,那么你应该使用迭代器适配器std::back_insert_iterator例如

replace_copy(s.begin(), s.end(), std::back_inserter( result ), ',', '.');

【讨论】:

以上是关于与 std::string 一起使用的 std::replace_copy 的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能将 std::string 与 libcurl 一起使用?

将 TaskDialogConfig 与 std::string 一起使用

将 std::string_view 与 api 一起使用,期望以 null 终止的字符串

将 std::string_view 与 api 一起使用,期望以 null 终止的字符串

将 SWIG 与将 std::string 作为参数的方法一起使用

如何将 std::string 转换为大写? [复制]