::tolower с использованием std::transform

Почему 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);

Я не хочу, чтобы трансформация происходила на месте.

Алгоритмы C++ присваивают значения диапазонам. Там должен быть диапазон для назначения. Пустая строка — это пустой диапазон. Имейте в виду, однако, что в вашем фрагменте кода есть немного неопределенного поведения.

StoryTeller - Unslander Monica 22.11.2022 16:19

Вам нужно пройти back_inserter.

康桓瑋 22.11.2022 16:20
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
110
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы пишете в лишней памяти, так как диапазон 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 еще хуже: «поведение std::tolower не определено, если значение аргумента не может быть представлено как unsigned char и не равно EOF», требуется некоторое приведение к unsigned char/ из int.
Jarod42 22.11.2022 16:36

@ Jarod42 Это относится как к ::tolower, так и к std::tolower, а также ко всем функциям из <cctype>/<ctype.h>. Практически всегда было бы неправильно вызывать эти функции без приведения, но кажется, что часто не-ASCII-вариант просто игнорируется.

user17732522 22.11.2022 18:35

@StackDanny Если вы хотите, чтобы это было правильно даже для символов, отличных от ASCII, то auto или char не подойдут. Параметр должен иметь тип unsigned char (или он должен быть приведен к нему перед передачей в tolower). Я пренебрег этим в своем предыдущем удаленном комментарии.

user17732522 22.11.2022 18:41

Думаю, я все время безрассудно использовал tolower. Интересно, почему было принято решение не определять для std::string::value_type, следовательно, char.

Stack Danny 22.11.2022 18:52

Один из способов — изменить размер строки, чтобы выделить достаточно памяти для ее заполнения 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, передается напрямую. Вместо этого следует передать лямбду, вызывающую функцию.

user17732522 22.11.2022 16:28

Другие вопросы по теме