Является ли замена всех const std::string & на std::string_view хорошим выбором?

Я очень чувствителен к распределению памяти и копированию. Итак, если функции требуется входной объект 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 не всегда хороший выбор?

«если где-то нужно использовать std::string, я использую .data() для получения строки». Я не понимаю. Если вам нужен std::string, вам нужно использовать std::string, а не что-то еще.

463035818_is_not_an_ai 28.08.2024 15:47

Связано: stackoverflow.com/a/72575650/1387438

Marek R 28.08.2024 16:04

«Я очень чувствителен к распределению памяти и копированию...» Я не думаю, что это должно быть в сообщении/вопросе.

user12002570 28.08.2024 16:08

Эта конкретная проблема все равно возникла бы, если бы вместо этого у вас был 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.

Artyer 28.08.2024 16:08

Если вашей функции нужно, чтобы строка std::string находилась где-то, передача как std::string_view будет более расточительной. Если вашей функции требуется, чтобы строка где-то заканчивалась нулевым const char *, передача as std::string_view будет нарушена.

Drew Dormann 28.08.2024 18:07

Название вашего вопроса не соответствует его телу. Заголовок выглядит как дубликат Когда бы мне передать const& std::string вместо std::string_view?, но ваш вопрос, похоже, больше касается использования std::string_view для поиска записи в std::unordered_map, ключи которой равны std::string, желательно без создания string.

JaMiT 29.08.2024 05:41
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
126
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

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 байт на ключ)

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