Szabolcs
Szabolcs

Reputation: 830

Why do C++ STL function calls need to be so verbose?

Why can't calls to STL functions be more brief? I was looking at the following code snippet on cppreference.com:

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

int main()
{
    std::string s("hello");
    std::transform(s.begin(), s.end(), s.begin(),
                   [](unsigned char c) { return std::toupper(c); });
    std::cout << s;
}

It seems to me that it should be possible to make this call more brief. The first obvious thing would be to do away with the lambda:

std::string s("hello");
std::transform(s.begin(), s.end(), s.begin(), std::toupper);
std::cout << s;

But this doesn't work. Since you usually want to convert a whole container it should be possible to just use that as a parameter:

std::string s("hello");
std::transform(s, s.begin(), std::toupper);
std::cout << s;

You could also omit the output iterator to return the result by value:

std::string s("hello");
std::cout << std::transform(s, std::toupper);

At this point the temporary variable is unnecessary:

std::cout << std::transform("hello"s, std::toupper);

With added readability:

using namespace std;
cout << transform("hello"s, toupper);

Wouldn't this be more readable and just nicer? Why weren't STL functions designed to allow writing brief code like this? Will it be possible to shorten these calls in a future version of the C++ standard?

Upvotes: 6

Views: 1479

Answers (2)

Jarod42
Jarod42

Reputation: 218098

Unfortunately, std::toupper has overloads, so the lambda is the workaround.

Then, with range-v3, you can simplify it to:

auto to_upper = [](unsigned char c) { return std::toupper(c); }; // To handle overloads
std::cout << std::string("Hello world" | ranges::view::transform(to_upper))
          << std::endl;

Demo

Upvotes: 7

Bo Persson
Bo Persson

Reputation: 92341

std::transform(s.begin(), s.end(), s.begin(),
               [](unsigned char c) { return std::toupper(c); });

The general form just works. Always!

You can use it to transform in-place or to a separate result:
std::transform(s.begin(), s.end(), result.begin(),....

It can transform the whole container, or just a part. Any part of your choice:
std::transform(s.begin() + i, s.begin() + j, s.begin() + i,...

And it works on any sequence, even if the elements are not part of a container.

So the general form would be needed anyway, as it is the most useful one.

Upvotes: 1

Related Questions