1

Converting a string can be done with

std::transform(s.begin(), s.end(), s.begin(), ::toupper);

but what's the best way to transform it into a new modified copy ?

tried :

string s1="text",s2;
std::transform(s1.begin(), s1.end(), s2.begin(), ::toupper);

but it fails at runtime with "Segmentation fault"

I managed to do it by :

string s1="text",s2;
s2 = s1;
std::transform(s2.begin(), s2.end(), s2.begin(), ::toupper);

and of course you can create a function to go char by char, but I don't think that's optimal, and I'm not learning anything from this...

Stefan Rogin
  • 1,499
  • 3
  • 25
  • 41
  • 5
    You need `std::back_inserter()`. – David G Nov 04 '14 at 22:27
  • You should be using `std::toupper` and ensuring that negative values are not passed into it if `char` is signed, lest you have undefined behaviour. – chris Nov 04 '14 at 22:33
  • @chris the example was actualy copy-pasted from the [cppreference](http://en.cppreference.com/w/cpp/algorithm/transform) site, so can std::string have unsigned char or negative values ? – Stefan Rogin Nov 04 '14 at 22:38
  • 1
    @Columbo why did you remove your answer ? – Stefan Rogin Nov 04 '14 at 22:40
  • 1
    @clickstefan Because I didn't like it. I post answers to help and didn't consider it helpful yet. – Columbo Nov 04 '14 at 22:43
  • @clickstefan, Yes, if `char` is signed, then `std::string` can certainly contain negative values unless you have a guarantee otherwise. And I did mean signed, not unsigned, sorry. Thought I fixed that quickly enough, but maybe not :/ – chris Nov 04 '14 at 22:43
  • @chris can you give a small example (sorry c/c++ rookie here) – Stefan Rogin Nov 04 '14 at 22:47
  • @clickstefan, I'd prefer the range-based for loop in the answer, but it's easily possible with a lambda (`[](unsigned char c) {return std::toupper(c);}`) or a function that looks the same. – chris Nov 04 '14 at 22:58
  • @clickstefan `sorry c/c++ rookie here` Well, I applaud you for using `std::transform` right off the bat, unlike other rookies. – PaulMcKenzie Nov 04 '14 at 23:02
  • @PaulMcKenzie yeah... but now it seems I have some code to remake :) I like c++, compared to c, but sometimes there are too many methods to get to the same thing, and it's hard to spot the good from the bad. – Stefan Rogin Nov 04 '14 at 23:07
  • someone posted an answer but I didn't get to comment : what's the difference between `s1{"text"}` and s1="text"? – Stefan Rogin Nov 04 '14 at 23:15

3 Answers3

5

std::transform doesn't know containers and will not insert new elements for you.
std::back_inserter provides an iterator that push_backs assigned values:

// DO NOT use this
std::transform(s1.begin(), s1.end(), std::back_inserter(s2), ::toupper);
//                                   ^^^^^^^^^^^^^^^^^^^^^^

However, you should not use the global version of toupper since it's deprecated.
Also you have to cast the argument to unsigned char first, or undefined behavior is easily encountered.

transform does not seem to fit here as you would need a lambda; use

for (auto ch : s1)
    s2 += std::toupper((unsigned char)ch);

Demo.

Community
  • 1
  • 1
Columbo
  • 60,038
  • 8
  • 155
  • 203
  • so the lambda use would be needed for the `unsigned char` problem ? so this makes `transform(str->begin(), str->end(), str->begin(), ::tolower);` invalid also ? – Stefan Rogin Nov 04 '14 at 23:16
  • @clickstefan A lambda would be needed to call `std::tolower` in the first place. And yes, the line with `transform` would be invalid. The problem is that simply calling (the non-local version of) `tolower` with `char` is generally wrong. – Columbo Nov 04 '14 at 23:28
1

Unless use of std::transform is a requirement, you can use:

string s1="text";
string s2 = s1;
for ( auto& ch : s2 )
   ch = toupper((unsigned char)ch);
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

I do not quite understand why one of the answers says

// DO NOT use this
std::transform(s1.begin(), s1.end(), std::back_inserter(s2), ::toupper);
//                                   ^^^^^^^^^^^^^^^^^^^^^^

push_back() is a method of std::string and it is perfectly fine to use std::transform with std::back_inserter

#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>

int main() {
  std::string s1 = "ABCDEFG";
  std::string s2;
  std::transform(s1.begin(), s1.end(), std::back_inserter(s2),
                 [](unsigned char uc) { return std::tolower(uc); });
  std::cout << s2 << std::endl; // abcdefg
  return 0;
}
aafulei
  • 2,085
  • 12
  • 27