0

I have the following small and easy code:

int main(int argc, char *argv[]) {
    std::vector<std::string> in;
    std::set<std::string> out;

    in.push_back("Hi");
    in.push_back("Dear");
    in.push_back("Buddy");

    for (const auto& item : in) {
***        std::transform(item.begin(),item.end(),item.begin(), ::tolower);
***        out.insert(item); 
    }    
    
    return 0;
}

I'd like to copy all items of in into out.

However, with an in-place lowercase conversion, preferrably without an extra temporary variable.

So this is the required content of out at the end:

hi
dear
buddy

Please note, const auto& item is fixed, meaning I can't remove the const requirement (this is part of a bigger library, here is over-simplified for demo).

How should I do this? (If I remove the "const" modifier, it works, but if modifications are not allowed on item, how can I still insert the item into the set, while also transforming it to lowercase?)

Daniel
  • 2,318
  • 2
  • 22
  • 53
  • 1
    Though it might not matter for your use, converting to lowercase one `char` at a time will not be correct for some languages other than English. – aschepler Nov 15 '21 at 18:59
  • It's fine if it can be done withing ASCII range. – Daniel Nov 15 '21 at 19:03
  • 1
    There is no way to do this in place if you can't get rid of the `const`. A temporary will be required for you to do the transformation on before adding it to the set. – NathanOliver Nov 15 '21 at 19:03
  • @Daniel this is very similar to your [earlier question](https://stackoverflow.com/questions/69977631/) about inserting lowercase strings into a `vector`. Why are you so determined to avoid temporaries before inserting? – Remy Lebeau Nov 15 '21 at 19:30

2 Answers2

1

Note, you have to copy - since items in the original in container can not be moved into out container. The below code makes the copy of each element exactly once.

...
in.push_back("Hi");
in.push_back("Dear");
in.push_back("Buddy");

std::transform(in.begin(), in.end(), std::inserter(out, out.end()),
               [] (std::string str) { boost::algorithm::to_lower(str); return str;}
              );    
return 0;
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Lambda argument `(std::string str)` creates a copy, no? – KamilCuk Nov 15 '21 at 19:03
  • 1
    Well, one **has** to have a copy, because one is expected to lowercase elements in `in`, without modifying original `in`. But this does not create more than one copy - the lower-case string is moved into the `set`. – SergeyA Nov 15 '21 at 19:04
0

You need a lambda function with transform, and you shouldn't have a const & to your strings or transform can't modify it.

#include <algorithm>
#include <set>
#include <string>
#include <vector>

int main() 
{
    std::vector<std::string> in;
    std::set<std::string> out;

    in.push_back("Hi");
    in.push_back("Dear");
    in.push_back("Buddy");

    for (/*const*/ auto& item : in)  // you can't have a const & if your going to modify it.
    {
        std::transform(item.begin(), item.end(), item.begin(), [](const char c) 
        { 
            return static_cast<char>(::tolower(c)); 
        });

        out.insert(item);
    }

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19