33

I have 2 std::string. I just want to, given the input string:

  1. capitalize every letter
  2. assign the capitalized letter to the output string.

How come this works:

  std::string s="hello";
  std::string out;
  std::transform(s.begin(), s.end(), std::back_inserter(out), std::toupper);

but this doesn't (results in a program crash)?

  std::string s="hello";
  std::string out;
  std::transform(s.begin(), s.end(), out.begin(), std::toupper);

because this works (at least on the same string:

  std::string s="hello";
  std::string out;
  std::transform(s.begin(), s.end(), s.begin(), std::toupper);
sivabudh
  • 31,807
  • 63
  • 162
  • 228
  • 5
    None of this examples compiles correctly in my case (gcc 4.7), i guess because `std::toupper` has been overloaded with a two arguments variant (provided by `locale.h`). I had to cast it explicitly: `std::transform(s.begin(), s.end(), s.begin(), (int(*)(int))std::toupper);` – Gab Apr 08 '14 at 09:21
  • That's still unspecified behavior, since `toupper` isn't explicitly mentioned to be addressable. – Noak Palander Mar 19 '23 at 15:34

3 Answers3

49

There is no space in out. C++ algorithms do not grow their target containers automatically. You must either make the space yourself, or use a inserter adaptor.

To make space in out, do this:

out.resize(s.length());

[edit] Another option is to create the output string with correct size with this constructor.

std::string out(s.length(), 'X');

hrnt
  • 9,882
  • 2
  • 31
  • 38
  • What's the difference between this and (Both are strings): std::string s2(s1); std::transform(s2.begin(), s2.end(), s2.begin(), std::toupper); ? Oh, I just answered my own question I think, in the first case it's just resizing the string. But in the second case I think it's equivalent? – Aaron H. Feb 11 '15 at 21:53
  • 1
    See the following: http://stackoverflow.com/questions/7131858/stdtransform-and-toupper-no-matching-function http://www.cplusplus.com/reference/iterator/back_inserter/ (though I will probably use resize myself for speed purposes) – Andrew Jul 17 '15 at 05:15
2

I'd say that the iterator returned by out.begin() is not valid after a couple of increments for the empty string. After the first ++ it's ==out.end(), then the behavior after the next increment is undefined.

After all this exactly what insert iterator is for.

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
  • 1
    Wouldn't .begin() equal .end() immediately for an empty string, rather than after the first increment? – Kylotan Sep 29 '09 at 13:44
0

Thats the sense of a backinserter: It inserts elements to a container. using begin(), you pass a iterator to a empty container and modify invalid iterators.

I am sorry - my edits interfered with your comments. I first posted something wrong accidentally.

RED SOFT ADAIR
  • 12,032
  • 10
  • 54
  • 92