Я очень чувствителен к распределению памяти и копированию. Итак, если функции требуется входной объект std::string, я всегда использую const std::string &.
Недавно я обнаружил, что const std::string & создаст объект std::string, если я передам char[]:
#include <iostream>
#include <string>
using namespace std;
void test_string(const std::string & s) { // use std::string_view here is better
// it can avoid local variable allocation
printf("%p\n", s.data());
}
int main() {
char aa[] = "asd";
test_string(aa);
printf("%p\n", aa);
}
Я изменил const std::string & на std::string_view, и это решило проблему ненужной конструкции или копирования. Итак, я думаю, что std::string_view достоин использования повсюду.
Я заменил все const std::string & на std::string_view. Если есть место, где мне нужно использовать std::string, я использую .data(), чтобы получить строку.
Проблемы возникают в следующей ситуации:
inline std::vector<std::string_view> Split(std::string_view str, std::string_view delim, const bool trim_empty = false) {
if (str.empty()) return {};
size_t pos, last_pos = 0, len;
std::vector<std::string_view> tokens;
while (true) {
pos = str.find(delim, last_pos);
if (pos == std::string::npos) pos = str.size();
len = pos - last_pos;
if (!trim_empty || len != 0) tokens.push_back(str.substr(last_pos, len));
if (pos == str.size()) break;
else last_pos = pos + delim.size();
}
return tokens;
}
Я использую это, чтобы разделить string на vector, как видите, std::string_view позволяет избежать копирования и выделения большого объема памяти.
Но проблема возникает при использовании std::string_view:
std::string str = "asd\nbn\ncd\n\n";
std::unordered_map<std::string, int> m;
m["asd"] = 2;
const auto & v = Split(str, "\n");
const auto & it = m.find(v.front().data());
Это не удалось, потому что v.front().data() возвращает всю строку, а не первую часть.
Я знаю, что это вызвано отсутствием "\0" в строковом потоке.
Кажется, нет хорошего способа найти правильный std::string_view, кроме как построить std::string с помощью std::string(v.front().data()).
Итак, могу ли я как-нибудь завершить .data() так, как ожидалось? Или замена const std::string & на std::string_view не всегда хороший выбор?
Связано: stackoverflow.com/a/72575650/1387438
«Я очень чувствителен к распределению памяти и копированию...» Я не думаю, что это должно быть в сообщении/вопросе.
Эта конкретная проблема все равно возникла бы, если бы вместо этого у вас был const std::string&. Если раньше у вас был std::vector<std::string> Split(const std::string& str, const std::string& delim, const bool trim_empty = false), он должен был стать std::vector<std::string> Split(std::string_view str, std::string_view delim, const bool trim_empty = false);, поскольку в векторе нет const std::string&. Также в C++20 вы можете использовать прозрачный unordered_map.
Если вашей функции нужно, чтобы строка std::string находилась где-то, передача как std::string_view будет более расточительной. Если вашей функции требуется, чтобы строка где-то заканчивалась нулевым const char *, передача as std::string_view будет нарушена.
Название вашего вопроса не соответствует его телу. Заголовок выглядит как дубликат Когда бы мне передать const& std::string вместо std::string_view?, но ваш вопрос, похоже, больше касается использования std::string_view для поиска записи в std::unordered_map, ключи которой равны std::string, желательно без создания string.





std::string_view::data() возвращает указатель, который не обязательно заканчивается нулем.std::string::data() возвращает указатель, который всегда завершается нулем (начиная с C++11).std::stringКонструктор 9, который принимает char*, требует, чтобы он указывал на строку с нулевым завершением.
Поскольку вы все равно создаете временный std::string, делайте это правильно: std::string{v.front()} создаст строку правильной длины из std::string_view.
Или, если это единственное использование Split, не используйте std::string_view вообще, не будет большой выгоды, если вы будете использовать string_view только для того, чтобы построить из него string.
Кроме того, помимо вопроса, но, как заметил Ботье в комментарии , вы используете ссылки неправильно. Оба объекта, на которые они ссылаются, умирают в конце соответствующей строки ;, и у вас остаются висящие ссылки.
Основная проблема здесь заключается в том, что вы создаете string из string_view с единственной целью использовать его с map.find, и в этом случае вы, вероятно, захотите вызвать конструктор string, который принимает string_view вместо передачи char*, чтобы он не передавал strlen. нужно вызвать std::string{the_string_view}. (сделайте std::unordered_map явно), но это не настоящее решение проблемы.
Обычно find имеет find, а компаратор и хеш не допускают других типов, поэтому string_view не работает для Key, если std::string есть std::string, вам придется определить свой собственный хэшер и компаратор, и вам понадобится C++20 для перегрузки, которая принимает любые введите, аналогичный ключу для найти
#include <unordered_map>
#include <string>
#include <string_view>
#include <iostream>
struct Equality
{
using is_transparent = std::true_type;
bool operator()(const auto& lhs, const auto& rhs) const {
auto result = lhs == rhs;
return result;
}
};
struct Hash
{
using is_transparent = std::true_type;
template <typename T>
size_t operator()(const T& obj) const {
auto result = std::hash<std::decay_t<T>>{}(obj);
return result;
}
};
int main()
{
std::unordered_map<std::string, int, Hash, Equality> m{ {"hel",1} };
std::string s = "hello";
std::string_view sv{ s };
sv = sv.substr(0, 3);
auto it = m.find(sv);
if (it != m.end())
{
std::cout << "found!";
}
}
Обратите внимание, что для запуска этого кода вам понадобится компилятор C++20, в противном случае вам придется использовать версию контейнера Boost, которая уже имеет эту перегрузку для более низких версий C++, или любую другую реализацию, имеющую эту перегрузку.
Последний вариант, если вы застряли на C++17 и не можете перейти на C++20 или использовать boost или любую другую библиотеку, — это создать собственный контейнер, который хранит std::string_view в наборе и соответствующий string_view в ключах карты, поэтому что вы можете использовать find в качестве аргумента string... но это расточительно для памяти (дополнительные 16 байт на ключ)
«если где-то нужно использовать std::string, я использую .data() для получения строки». Я не понимаю. Если вам нужен
std::string, вам нужно использоватьstd::string, а не что-то еще.