Преждевременная оптимизация — одна из худших идей. Просто перейдите к глубокой копии 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
.