Когда следует использовать std::string/std::string_view для параметра/типа возвращаемого значения

Введение

Я пишу какое-то приложение для связи. До C++17 (без Boost) я использовал std::string и его константную ссылку как cls1.

Начиная с C++17, я представил std::string_view в своем коде как cls2. Однако у меня нет четкой политики, когда я должен использовать std::string_view. Мое коммуникационное приложение получает данные из сети и сохраняет их в recv_buffer. И создает некоторые классы приложений из recv_buffer.

Строительство

Если я сосредоточусь только на конструкторе cls1, конструкция перемещения будет эффективной. Но думаю, что откуда параметр s. Если это исходно из recv_buffer, я могу создать std::string_view в приемной (очень ранней) точке. И пока recv_buffer включен, используйте std::string_view везде. Если мне нужно сохранить часть recv_buffer, создайте std::string.

Единственным исключением, которое я заметил, является то, что recv_buffer всегда содержит полные данные для моего класса приложения. В этом случае эффективна конструкция перемещения.

Добытчик

Я думаю, что использование возвращаемого типа как std::string_view имеет преимущество. Некоторые функции-члены, такие как substr(), эффективны. Но недостатков пока не вижу.

Вопрос

Подозреваю, что могу увидеть только плюсы std::string_view. Прежде чем переписывать много кодов, я хотел бы узнать ваши идеи.

PoC-код

#include <string>

struct cls1 {
    explicit cls1(std::string s):s_(std::move(s)) {}
    std::string const& get() const { return s_; }
private:
    std::string s_;
};

struct cls2 {
    explicit cls2(std::string_view s):s_(s) {}
    std::string_view get() const { return s_; }
private:
    std::string s_;
};

#include <iostream>

int main() {
    // If all of the receive buffer is the target
    {
        std::string recv_buffer = "ABC";
        cls1 c1(std::move(recv_buffer)); // move construct
        std::cout << c1.get().substr(1, 2) << std::endl; // create new string
    }
    {
        std::string recv_buffer = "ABC";
        cls2 c2(recv_buffer);            // copy happend
        std::cout << c2.get().substr(1, 2) << std::endl; // doesn't create new string
    }

    // If a part of the receive buffer is the target
    {
        std::string recv_buffer = "<<<ABC>>>";
        cls1 c1(recv_buffer.substr(3, 3)); // copy happend and move construct
        std::cout << c1.get().substr(1, 2) << std::endl; // create new string
    }
    {
        std::string recv_buffer = "<<<ABC>>>";
        std::string_view ref = recv_buffer;
        cls2 c2(ref.substr(3, 3)); // string create from the part of buffer directly
        std::cout << c2.get().substr(1, 2) << std::endl; // doesn't create new string
    }
}

Запуск демонстрации: https://wandbox.org/permlink/TW8w3je3q3D46cjk


person Takatoshi Kondo    schedule 18.06.2019    source источник
comment
Кто-то задал очень похожий вопрос несколько дней назад: методы, возвращающие const stdstring, возвращают представление const stdstring вместо "> stackoverflow.com/questions/56601261/   -  person R2RT    schedule 18.06.2019
comment
Рассматривайте string_view как ссылку. Если объект, на который он ссылается, исчезает, у вас проблема.   -  person Michael Chourdakis    schedule 18.06.2019
comment
Также в std::string_view отсутствуют все методы-модификаторы, которые есть в std::string (которые вам не нужны при работе с const ref), std::string::c_str() также.   -  person YSC    schedule 18.06.2019


Ответы (2)


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

Рассматривайте его как константную ссылку. Если объект, на который он ссылается, исчезает по какой-либо причине, у вас есть проблема. Если ваш код может вернуть ссылку, вы можете вернуть string_view.

Пример:

#include <cstdio>
#include <string>
#include <vector>
#include <string.h>
#include <iostream>

int main()
{
    char* a = new char[10];
    strcpy(a,"Hello");
    std::string_view s(a);
    std::cout << s; // OK    
    delete[] a;
    std::cout << s;     // whops. UD. If it was std::string, no problem, it would have been a copy
}

Подробнее.

person Michael Chourdakis    schedule 18.06.2019
comment
Благодарю вас! Я понимаю насчет жизни. Он похож на std::string const& и std::string_view. Если возможно и то, и другое, и мне не нужно нулевое завершение, я выбираю std::string_view. Я решил, что это моя политика. Одно исключение завершено, std::string всегда можно указать в качестве аргумента конструктора. В этом случае я использую std::string или std::string&& в качестве параметра конструктора. - person Takatoshi Kondo; 18.06.2019
comment
Я предполагаю, что я бы использовал строковое представление только тогда, когда мне приходится иметь дело с char*. Если бы у меня уже был std::string, я бы перенес его в string_view, только если бы мне нужна была подстрока. - person Michael Chourdakis; 18.06.2019
comment
Верно. Я обобщил свой вопрос, когда впервые спросил, но на самом деле recv_buffer - это пакет MQTT, такой как docs.oasis-open.org/mqtt/mqtt/v5.0/os/ . В этом случае я думаю, что подход на основе std::string_view лучше получить строку a/b и c/d из recv_bffer . Как вы упомянули, это своего рода случай подстроки. - person Takatoshi Kondo; 18.06.2019

Не возвращайте строковое представление, когда:

  • Вызывающей стороне нужна строка с завершающим нулем. Это часто бывает при работе с C API.
  • Вы нигде не храните саму строку. В этом случае вы сохраняете строку в члене.

Поймите, что представление строки становится недействительным из-за операций над исходной строкой, таких как изменение емкости, а также в случае уничтожения исходной строки. Если вызывающему объекту строка нужна дольше, чем время жизни объекта, в котором хранится строка, он может скопировать из представления в свое собственное хранилище.

person eerorika    schedule 18.06.2019
comment
Благодарю вас! Я понимаю минусы std::string_view. 1-й пункт разумен. Я понимаю, что 2-й пункт означает Не возвращать ссылку на локальный временный объект. В этом случае тип возврата std::string. И std::string_view, и std::string const& плохие. Это правильно? - person Takatoshi Kondo; 18.06.2019
comment
@TakatoshiKondo Возврат строкового представления или ссылки на локальную строку был бы плохим, да. - person eerorika; 18.06.2019