user3123061
user3123061

Reputation: 769

Weird behavior of std::replace_copy used with std::string

i have this simple code:

#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;
}

Console output of this program with result.resize line uncommented is:

"1.0"
"1.0"
3

-thats Ok, but when line with result.resize is commented-out, output is :

""
"1.0"
0

-this can leads into weird errors because result != result.c_str() !!!

Can this behavior of replace_copy (and posibly also similar templates) may be considered as error in standard library? I cannot find anything relevant to this subject. Thanks.

Compiler: mingw32-g++ 4.7.1

Upvotes: 0

Views: 565

Answers (2)

Vlad from Moscow
Vlad from Moscow

Reputation: 310930

When you used statement

result.resize(s.length());

you created and initialized (more precisely assigned) the string with three elements with values '\0'. When this statement was not used the string had no relements and the behaviour of the program was undefined.In fact the code with uncommented line is equivalent to the following:

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

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

If to write as it was with the commented statement then you should use iterator adapter std::back_insert_iteratorFor example

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

Upvotes: 1

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385098

What did you expect?

Without the resize, there is no space in your string for the new characters.

Trying to copy into that space regardless will definitely result in "weird" [read: undefined] behaviour. You're mangling your memory.

replace_copy copies to a target range, which is not the same as inserting new elements into a target container. The range has to already exist…

… unless you use a back_inserter, which functions as a sort of fake range, that actually performs insertion under the hood:

#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

Warning! Getting the right output in that live demo does not prove anything, as undefined behaviour can occasionally "appear to work". However, I have 96k rep and you can trust me. ;)

Upvotes: 5

Related Questions