Каков наиболее эффективный способ преобразования QStringView в std::string?

У меня есть функция, которая получает аргумент как QStringView. Среди прочего, это должно быть преобразовано в std::string. QString для этого есть функция toStdString(). Есть ли способ избежать потенциально дорогостоящего промежуточного шага с помощью QString?

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

Ответы 3

Преждевременная оптимизация — одна из худших идей. Просто перейдите к глубокой копии QString, а затем преобразуйте в std::string.

Однако, если по какой-то причине вы хотите возиться с этим, у вас есть несколько вариантов.

Имеет смысл переключиться на toWCharArray или изменить кодировку с помощью toUtf8. Вариант без копирования, это просто взять необработанные данные .utf16() из QStringView и создать std::u16string_view из этого указателя.

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

Преобразование между кодировками символов трудно сделать правильно. QString хранит 16-битный UTF-16. toStdString перекодирует это как utf-8, используя промежуточный массив байтов, используя toUtf8.

QStringView также имеет toUtf8, который возвращает QByteArray и имеет те же гарантии, что и QString.

std::string toStdString( QStringView view ) {
  auto bytes = view.toUtf8(); // allocates and converts.
  return {bytes.constData(), bytes.length()}; // copies to a std::string
}

это сбрасывает 1 копию памяти по сравнению с наивным

std::string toStdString( QStringView view ) {
  return QString(view).toStdString();
}

есть 1 промежуточная копия памяти, которую теоретически тоже можно удалить; вы можете напрямую преобразовать данные UTF16 QStringView в буфер в std::string.

std::string toStdString( QStringView view ) {
  auto toUtf8 = QStringEncoder(QStringEncoder::Utf8);
  auto space = toUtf8.requiredSpace(view.length());
  std::string retval;
  // make a string of all nulls:
  retval.resize(space+1); // +1 probably not needed
  // output the UTF8 into the std::string:
  char* end = toUtf8.appendToBuffer(retval.data(), view);
  // Strip the nulls logically from the returned string:
  retval.resize(end-retval.data());
  return retval;
}

это попытка избежать выделения промежуточного буфера. Он может быть неверным и иметь ошибки, но дизайн добротный.

Теоретически возможна еще более безумная система, которая вычисляет фактическое пространство, необходимое для UTF16, до того, как (или как вы) создадите выходной буфер.

Это дополнение к принятому ответу от пользователя Yakk - Adam Nevraumont.

Я проверил unittest, что все преобразования эквивалентны. Затем я написал (Google Benchmark) тесты для различных методов. Я хотел бы поделиться результатами здесь.

#include <benchmark/benchmark.h>
#include <QString>
#include <QStringView>
#include <QStringEncoder>

std::string qStringToStdString(QString const& data) {
    return data.toStdString();
}

std::string qStringViewToStdString1(QStringView const data) {
    return data.toString().toStdString();
}

std::string qStringViewToStdString2(QStringView const data) {
    auto const bytes = data.toUtf8();
    return std::string(bytes.constData(), bytes.length());
}

std::string qStringViewToStdString3(QStringView const data) {
    auto toUtf8 = QStringEncoder(QStringEncoder::Utf8);
    auto len = toUtf8.requiredSpace(data.length());
    std::string result;
#ifdef __cpp_lib_string_resize_and_overwrite
    result.resize_and_overwrite(len, [len](char*, std::size_t) { return len; });
#else
    result.resize(len);
#endif
    char* end = toUtf8.appendToBuffer(result.data(), data);
    result.resize(end - result.data());
    return result;
}

static void qt_to_std(benchmark::State& state) {
    QString test(state.range(0) - 1, 'A');
    for (auto _ : state) {
        auto str = cadapt::qt_to_std<char>(test);
        benchmark::DoNotOptimize(str);
    }
}

static void qt_view_to_std_1(benchmark::State& state) {
    QString data(state.range(0) - 1, 'A');
    QStringView test(data);
    for (auto _ : state) {
        auto str = qStringViewToStdString1(test);
        benchmark::DoNotOptimize(str);
    }
}

static void qt_view_to_std_2(benchmark::State& state) {
    QString data(state.range(0) - 1, 'A');
    QStringView test(data);
    for (auto _ : state) {
        auto str = qStringViewToStdString2(test);
        benchmark::DoNotOptimize(str);
    }
}

static void qt_view_to_std_3(benchmark::State& state) {
    QString data(state.range(0) - 1, 'A');
    QStringView test(data);
    for (auto _ : state) {
        auto str = qStringViewToStdString3(test);
        benchmark::DoNotOptimize(str);
    }
}

BENCHMARK(qt_to_std)
    ->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12)->Arg(16)
    ->Arg(20)->Arg(32)->Arg(64)->Arg(128)->Arg(256)
    ->Arg(512)->Arg(1024)->Arg(2048)->Arg(4096)
    ->Arg(8192)->Arg(16384)->Arg(32768)->Arg(65536);

BENCHMARK(qt_view_to_std_1)
    ->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12)->Arg(16)
    ->Arg(20)->Arg(32)->Arg(64)->Arg(128)->Arg(256)
    ->Arg(512)->Arg(1024)->Arg(2048)->Arg(4096)
    ->Arg(8192)->Arg(16384)->Arg(32768)->Arg(65536);

BENCHMARK(qt_view_to_std_2)
    ->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12)->Arg(16)
    ->Arg(20)->Arg(32)->Arg(64)->Arg(128)->Arg(256)
    ->Arg(512)->Arg(1024)->Arg(2048)->Arg(4096)
    ->Arg(8192)->Arg(16384)->Arg(32768)->Arg(65536);

BENCHMARK(qt_view_to_std_3)
    ->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12)->Arg(16)
    ->Arg(20)->Arg(32)->Arg(64)->Arg(128)->Arg(256)
    ->Arg(512)->Arg(1024)->Arg(2048)->Arg(4096)
    ->Arg(8192)->Arg(16384)->Arg(32768)->Arg(65536);
QStringView=>QString.toStdString  => qt_view_to_std_1
QStringView=>QStringEncoder/C++20 => qt_view_to_std_3 (resize)
QString.toStdString               => qt_to_std
QStringView=>toUtf8               => qt_view_to_std_2
QStringView=>QStringEncoder/C++23 => qt_view_to_std_3 (resize_and_overwrite)
Length  qt_view_to_std_1  qt_view_to_std_3  qt_to_std  qt_view_to_std_2  qt_view_to_std_3
                                     C++20                                          C++23
     4             79 ns             15 ns      42 ns             35 ns             11 ns
     6             79 ns             30 ns      44 ns             36 ns             28 ns
     8             80 ns             31 ns      50 ns             37 ns             29 ns
    10             81 ns             32 ns      40 ns             38 ns             31 ns
    12             82 ns             34 ns      40 ns             39 ns             32 ns
    16             84 ns             37 ns      48 ns             41 ns             36 ns
    20            104 ns             28 ns      54 ns             49 ns             27 ns
    32            110 ns             38 ns      62 ns             55 ns             37 ns
    64            110 ns             40 ns      62 ns             57 ns             39 ns
   128            117 ns             44 ns      67 ns             63 ns             42 ns
   256            128 ns             53 ns      73 ns             69 ns             49 ns
   512            193 ns             85 ns     105 ns            113 ns             83 ns
  1024            232 ns            134 ns     137 ns            146 ns            113 ns
  2048            339 ns            229 ns     232 ns            239 ns            182 ns
  4096            516 ns            412 ns     374 ns            379 ns            302 ns
  8192           1504 ns            853 ns     762 ns            729 ns            549 ns
 16384           2317 ns           1807 ns    1602 ns           1571 ns           1134 ns
 32768          25243 ns           3816 ns    3148 ns           3082 ns           2223 ns
 65536          70484 ns           7869 ns    6987 ns           6465 ns           4512 ns

(Чем меньше, тем лучше)

Преобразование из QStringView через QString в std::string, безусловно, самое медленное. Особенно для больших строк это на порядок (в 10 раз) медленнее, чем от QString до std::string.

Метод через QStringEncoder работает лучше всего, но только если он использует оптимизацию C++ 23 через resize_and_overwrite. Таким образом, память не инициализируется при изменении размера. В режиме C++20 он заполняется нулями, что съедает много времени при работе с большими строками.

Для строк небольшого размера нет существенной разницы между C++20 resize и C++23 resize_and_overwrite.

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