Почему std::transform не работает так:
std::string tmp = "WELCOME";
std::string out = "";
std::transform(tmp.begin(), tmp.end(), out.begin(), ::tolower);
снаружи пусто!
Но это работает:
std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
Я не хочу, чтобы трансформация происходила на месте.
Вам нужно пройти back_inserter.





Вы пишете в лишней памяти, так как диапазон out меньше, чем у tmp. Вы можете сохранить результат в out, применив std::back_inserter.
Как указал пользователь 17732522, поскольку незаконно брать адрес стандартной библиотечной функции, лучше передать лямбда-объект, который при необходимости вызывает std::tolower для персонажа.
std::transform(tmp.begin(), tmp.end(), std::back_inserter(out), [](auto c) {
return std::tolower(static_cast<unsigned char>(c));
});
std::tolower не определено, если значение аргумента не может быть представлено как unsigned char и не равно EOF», требуется некоторое приведение к unsigned char/ из int.
@ Jarod42 Это относится как к ::tolower, так и к std::tolower, а также ко всем функциям из <cctype>/<ctype.h>. Практически всегда было бы неправильно вызывать эти функции без приведения, но кажется, что часто не-ASCII-вариант просто игнорируется.
@StackDanny Если вы хотите, чтобы это было правильно даже для символов, отличных от ASCII, то auto или char не подойдут. Параметр должен иметь тип unsigned char (или он должен быть приведен к нему перед передачей в tolower). Я пренебрег этим в своем предыдущем удаленном комментарии.
Думаю, я все время безрассудно использовал tolower. Интересно, почему было принято решение не определять для std::string::value_type, следовательно, char.
Один из способов — изменить размер строки, чтобы выделить достаточно памяти для ее заполнения std::transform.
out.resize(tmp.size()); // <---
std::transform(tmp.begin(), tmp.end(), out.begin(), ::tolower);
В качестве альтернативы, как уже упоминалось, вы можете использовать std::back_inserter, который обрабатывает вставку для вас. Мы также можем вызвать out.reserve, чтобы сэкономить время на выделении памяти (особенно если ваша строка большая).
out.reserve(tmp.size()); // (optional) Smol optimisation.
std::transform(tmp.begin(), tmp.end(), std::back_inserter(out), ::tolower);
Обычно это работает, но поведение по-прежнему технически не определено, если функция стандартной библиотеки, такая как ::tolower, передается напрямую. Вместо этого следует передать лямбду, вызывающую функцию.
Алгоритмы C++ присваивают значения диапазонам. Там должен быть диапазон для назначения. Пустая строка — это пустой диапазон. Имейте в виду, однако, что в вашем фрагменте кода есть немного неопределенного поведения.