std::string vs const std::string& vs std::string_viewЗахват аргументовC++:...



std::string vs const std::string& vs std::string_view


Захват аргументовC++: std::vector.push_back() и классыКонвертация std::string в std::wstringИспользование std::string в dllВключение шаблонов и std::enable_ifИспользование std::move в списках инициализацииКак передать аргумент в std::thread по ссылке?Специализация шаблонной функции для std::stringC++,оптимизация подобия функции strstr(const char*,const char*)Запись текста в std::string дважды. С++Отличие между const string &s и string &s













2















Что правильнее передавать как аргумент функции(например в конструктор), если там эта строка будет просто скопирована?










поделиться|улучшить этот вопрос



























    2















    Что правильнее передавать как аргумент функции(например в конструктор), если там эта строка будет просто скопирована?










    поделиться|улучшить этот вопрос

























      2












      2








      2


      1






      Что правильнее передавать как аргумент функции(например в конструктор), если там эта строка будет просто скопирована?










      поделиться|улучшить этот вопрос














      Что правильнее передавать как аргумент функции(например в конструктор), если там эта строка будет просто скопирована?







      c++






      поделиться|улучшить этот вопрос













      поделиться|улучшить этот вопрос











      поделиться|улучшить этот вопрос




      поделиться|улучшить этот вопрос










      задан 12 часов назад









      tim barstim bars

      607




      607






















          3 ответа
          3






          текущие

          по дате публикации

          голоса


















          1














          Использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий. И это относится не только к std::string и std::string_view



          Для начала определимся с реализацией наших сущностей. Пусть std::string построен на трех указателях по 4 байта каждый (или указатель и два размера):



          class string
          {
          //...
          pointer * m_begin; //Начало строки
          pointer * m_end; //Указатель за последний элемент
          pointer * m_end_of_storage; //Указатель за конец выделенной памяти
          };


          std::string_view при этом реализован с помощью двух указателей (или указатель и размер):



          class string_view
          {
          //...
          pointer m_data; //Указатель на начало данных
          pointer m_end; //Указатель за конец данных
          };


          Упрощенно рассмотрим три версии простого кода:



          void other(std::string); //<-- функция, в которую всегда передается копия.

          void run(string_view v) //<-- Функция, которая вызывает функцию other
          {
          //т.е. эта функция делает ту самую копию объекта
          other(v);
          }

          void general(string str)
          {
          //А из этой функции в функцию run передается строка
          other(str);
          }


          Что будет сгенерировано компилятором и как рассматривать не будем, т.к. это сильно зависит от компилятора и параметров компиляции, но постараемся посмотреть что может быть и как.



          В нашем случае other передает копию string_view, т.е. general копирует два указателя (не говорим о возможности вызова конструктора копирования, подразумевая, что он самый тривиальный и в результирующем коде приведет просто к копированию двух членов класса). Затем строка копируется из string_view. Ничего сложного.



          Теперь возьмем тот же код, но при этом run будет принимать ссылку на string.



          void run(string const & v)
          {
          other(v);
          }

          void general(string str)
          {
          other(str);
          //str используется, её нельзя перемещать в other
          }


          Да, Вы правы, передача ссылки в функцию run - быстрая операция, можно считать, что это копирование указателя. Но у передачи ссылки есть и минусы. Один из них - функция run обращается к строке через ссылку, т.е. появляется дополнительный уровень косвенности, который может вылиться в большие затраты, чем "лишнее" копирование указателя в версии функции, принимающей string_view.



          Теперь рассмотрим третий вариант функции run:



          void run(string v)
          {
          other(std::move(v));
          }


          В данном случае копирование строки происходит еще в функции general (причем мы не перемещаем параметр str, т.к. он где-то там еще нужен далее). Затем функция run перемещает объект v в параметр функции other, а это в нашем случае приводит к копированию трех указателей и занулению старых. Очевидно, что в этом случае операций намного больше, чем со string_view, и, вполне вероятно, что косвенное обращение будет тоже быстрее. То есть это может быть самым тормознутым вариантом.



          А теперь представим, такую ситуацию:



          void run(string const &); //<-- имеется такая run
          void run(string_view); //или такая, нам без разницы

          class SomeClass
          {
          string m_text;
          mutex m_mutex;
          void SomeClass::call()
          {
          //Мы знаем, что run делает копию строки,
          //и запускает поток для её обработки
          lock_quard<mutex> locker;//объект у нас защищен мьютексом
          run(m_text);//Где-то там строка копируется и запускается новый поток
          //мы не знаем точный момент, когда строка скопируется,
          //поэтому вынуждены ждать выполнения run под защитой мьютекса,
          //т.к. в other содержаться указатели на защищенные члены нашего класса.
          //Вполне вероятно, что в этот момент другие
          //потоки уже хотят работать с нашим объектом,
          //но не могут, т.к. мьютекс захвачен.
          }
          };


          Я в комментариях описал проблему. А теперь возьмем ситуацию с копированием и перемещением:



          void run(string); //<-- теперь run принимает копию

          class SomeClass
          {
          //..
          void SomeClass::call()
          {
          //Мы знаем, что run принимает копию строки
          //и запускает поток для её обработки
          unique_lock<mutex> locker;//объект у нас защищен мьютексом
          string text_copy = m_text;//Под защитой выполняем копирование строки
          locker.unlock();//И разблокируем мьютекс, т.к.
          run(std::move(text_copy));//нам уже без разницы что-том делает run,
          //копия данных для него уже создана и другим потокам можно дать доступ к объекту
          }
          };


          У string_view при этом тоже имеются свои прелести. string_view в принципе не привязан к string - это просто данные и их размер. Т.е. string_view может работать не только с string:



          void run(string_view v);

          void general(string str)
          {
          other(str);//ok
          }

          void general(char const * str, size_t len)
          {
          other(string_view(str, len));//ok
          }

          void general(char const * str)
          {
          other(string_view(str));//ok
          }

          void general(std::vector<char> const & v)
          {
          other(string_view(v.data(), v.size()));//ok
          }


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



          Но оптимизации также могут перевернуть всё с ног на голову. Когда нужно сделать копию строки, кажется, что это приведет к аллокации памяти и всё это будет медленно. В общем случае это верно, но есть такая штука, как SSO (small string optimization), которая позволяет хранить маленькие строки прямо в объекте string, используя сам объект как буфер для строки. В случае применения такой оптимизации копирование может оказаться равноценно или даже дешевле перемещения.



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




          а для чего тогда стоит использовать string_view?




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



          Всё сказанное выше весьма условно и служит только для демонстрации различных ситуаций.



          Как видите, применение того или иного средства полностью зависит от конкретных условий использования, применяемых алгоритмов, оптимизаций компилятора, устройства библиотеки, платформы на которой всё это работает и т.д., поэтому закончу тем, с чего начал - использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий.






          поделиться|улучшить этот ответ

































            2














            Просто std::string. А потом вместо компирования - перемещать через std::move.






            поделиться|улучшить этот ответ
























            • а для чего тогда стоит использовать string_view? И чем это лучше чем константная ссылка, с ней то по идее будет вызван только copy конструктор

              – tim bars
              12 часов назад













            • это всё весьма и весьма сомнительно.

              – Croessmah
              12 часов назад











            • @timbars "чем это лучше" Тем, что при таком варианте мы можем в некоторых случаях избежать копирования - когда передаем в функцию временный объект (rvalue). string_view, имхо, нужен для функций, анализирующих строки или сегменты строк (при парсинге и т. п.). Как универсальный "параметр-строка" он не особо прижился, имхо, потому что часто требудется, чтобы параметр был null-terminated, чего string_view не дает.

              – HolyBlackCat
              12 часов назад













            • @Croessmah Расска́жите подробнее?

              – HolyBlackCat
              12 часов назад



















            1
















            1. Если строка будет "просто скопирована", то вам следует реализовывать либо "ленивый" вариант семантики перемещения (передавать std::string по значению и затем делать из него перемещение), либо "полный" вариант семантики перемещения (писать две перегруженных функции: для const std::string & и для std::string &&), либо, возможно, реализовать forwarding (писать шаблонную функцию, принимающую универсальную сслыку и делающую std::forward в вашу копию).



              См. https://ru.stackoverflow.com/a/822789/182825



            2. std::string_view уместен везде, где вы будете просто анализировать строку, т.е. он является заменителем const std:string & в ситуациях, когда копирование не будет делаться.







            поделиться|улучшить этот ответ























              Ваш ответ






              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "609"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: false,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "на платформе u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "Пользовательский контент попадает под действие u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003eлицензии cc by-sa 3.0u003c/au003e с u003ca href="https://stackoverflow.com/legal/content-policy"u003eуказанием ссылки на источникu003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              черновик сохранён

              черновик удалён


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fru.stackoverflow.com%2fquestions%2f954084%2fstdstring-vs-const-stdstring-vs-stdstring-view%23new-answer', 'question_page');
              }
              );

              Отправить без регистрации















              Необходима, но никому не показывается

























              3 ответа
              3






              текущие

              по дате публикации

              голоса








              3 ответа
              3






              текущие

              по дате публикации

              голоса









              текущие

              по дате публикации

              голоса






              текущие

              по дате публикации

              голоса









              1














              Использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий. И это относится не только к std::string и std::string_view



              Для начала определимся с реализацией наших сущностей. Пусть std::string построен на трех указателях по 4 байта каждый (или указатель и два размера):



              class string
              {
              //...
              pointer * m_begin; //Начало строки
              pointer * m_end; //Указатель за последний элемент
              pointer * m_end_of_storage; //Указатель за конец выделенной памяти
              };


              std::string_view при этом реализован с помощью двух указателей (или указатель и размер):



              class string_view
              {
              //...
              pointer m_data; //Указатель на начало данных
              pointer m_end; //Указатель за конец данных
              };


              Упрощенно рассмотрим три версии простого кода:



              void other(std::string); //<-- функция, в которую всегда передается копия.

              void run(string_view v) //<-- Функция, которая вызывает функцию other
              {
              //т.е. эта функция делает ту самую копию объекта
              other(v);
              }

              void general(string str)
              {
              //А из этой функции в функцию run передается строка
              other(str);
              }


              Что будет сгенерировано компилятором и как рассматривать не будем, т.к. это сильно зависит от компилятора и параметров компиляции, но постараемся посмотреть что может быть и как.



              В нашем случае other передает копию string_view, т.е. general копирует два указателя (не говорим о возможности вызова конструктора копирования, подразумевая, что он самый тривиальный и в результирующем коде приведет просто к копированию двух членов класса). Затем строка копируется из string_view. Ничего сложного.



              Теперь возьмем тот же код, но при этом run будет принимать ссылку на string.



              void run(string const & v)
              {
              other(v);
              }

              void general(string str)
              {
              other(str);
              //str используется, её нельзя перемещать в other
              }


              Да, Вы правы, передача ссылки в функцию run - быстрая операция, можно считать, что это копирование указателя. Но у передачи ссылки есть и минусы. Один из них - функция run обращается к строке через ссылку, т.е. появляется дополнительный уровень косвенности, который может вылиться в большие затраты, чем "лишнее" копирование указателя в версии функции, принимающей string_view.



              Теперь рассмотрим третий вариант функции run:



              void run(string v)
              {
              other(std::move(v));
              }


              В данном случае копирование строки происходит еще в функции general (причем мы не перемещаем параметр str, т.к. он где-то там еще нужен далее). Затем функция run перемещает объект v в параметр функции other, а это в нашем случае приводит к копированию трех указателей и занулению старых. Очевидно, что в этом случае операций намного больше, чем со string_view, и, вполне вероятно, что косвенное обращение будет тоже быстрее. То есть это может быть самым тормознутым вариантом.



              А теперь представим, такую ситуацию:



              void run(string const &); //<-- имеется такая run
              void run(string_view); //или такая, нам без разницы

              class SomeClass
              {
              string m_text;
              mutex m_mutex;
              void SomeClass::call()
              {
              //Мы знаем, что run делает копию строки,
              //и запускает поток для её обработки
              lock_quard<mutex> locker;//объект у нас защищен мьютексом
              run(m_text);//Где-то там строка копируется и запускается новый поток
              //мы не знаем точный момент, когда строка скопируется,
              //поэтому вынуждены ждать выполнения run под защитой мьютекса,
              //т.к. в other содержаться указатели на защищенные члены нашего класса.
              //Вполне вероятно, что в этот момент другие
              //потоки уже хотят работать с нашим объектом,
              //но не могут, т.к. мьютекс захвачен.
              }
              };


              Я в комментариях описал проблему. А теперь возьмем ситуацию с копированием и перемещением:



              void run(string); //<-- теперь run принимает копию

              class SomeClass
              {
              //..
              void SomeClass::call()
              {
              //Мы знаем, что run принимает копию строки
              //и запускает поток для её обработки
              unique_lock<mutex> locker;//объект у нас защищен мьютексом
              string text_copy = m_text;//Под защитой выполняем копирование строки
              locker.unlock();//И разблокируем мьютекс, т.к.
              run(std::move(text_copy));//нам уже без разницы что-том делает run,
              //копия данных для него уже создана и другим потокам можно дать доступ к объекту
              }
              };


              У string_view при этом тоже имеются свои прелести. string_view в принципе не привязан к string - это просто данные и их размер. Т.е. string_view может работать не только с string:



              void run(string_view v);

              void general(string str)
              {
              other(str);//ok
              }

              void general(char const * str, size_t len)
              {
              other(string_view(str, len));//ok
              }

              void general(char const * str)
              {
              other(string_view(str));//ok
              }

              void general(std::vector<char> const & v)
              {
              other(string_view(v.data(), v.size()));//ok
              }


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



              Но оптимизации также могут перевернуть всё с ног на голову. Когда нужно сделать копию строки, кажется, что это приведет к аллокации памяти и всё это будет медленно. В общем случае это верно, но есть такая штука, как SSO (small string optimization), которая позволяет хранить маленькие строки прямо в объекте string, используя сам объект как буфер для строки. В случае применения такой оптимизации копирование может оказаться равноценно или даже дешевле перемещения.



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




              а для чего тогда стоит использовать string_view?




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



              Всё сказанное выше весьма условно и служит только для демонстрации различных ситуаций.



              Как видите, применение того или иного средства полностью зависит от конкретных условий использования, применяемых алгоритмов, оптимизаций компилятора, устройства библиотеки, платформы на которой всё это работает и т.д., поэтому закончу тем, с чего начал - использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий.






              поделиться|улучшить этот ответ






























                1














                Использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий. И это относится не только к std::string и std::string_view



                Для начала определимся с реализацией наших сущностей. Пусть std::string построен на трех указателях по 4 байта каждый (или указатель и два размера):



                class string
                {
                //...
                pointer * m_begin; //Начало строки
                pointer * m_end; //Указатель за последний элемент
                pointer * m_end_of_storage; //Указатель за конец выделенной памяти
                };


                std::string_view при этом реализован с помощью двух указателей (или указатель и размер):



                class string_view
                {
                //...
                pointer m_data; //Указатель на начало данных
                pointer m_end; //Указатель за конец данных
                };


                Упрощенно рассмотрим три версии простого кода:



                void other(std::string); //<-- функция, в которую всегда передается копия.

                void run(string_view v) //<-- Функция, которая вызывает функцию other
                {
                //т.е. эта функция делает ту самую копию объекта
                other(v);
                }

                void general(string str)
                {
                //А из этой функции в функцию run передается строка
                other(str);
                }


                Что будет сгенерировано компилятором и как рассматривать не будем, т.к. это сильно зависит от компилятора и параметров компиляции, но постараемся посмотреть что может быть и как.



                В нашем случае other передает копию string_view, т.е. general копирует два указателя (не говорим о возможности вызова конструктора копирования, подразумевая, что он самый тривиальный и в результирующем коде приведет просто к копированию двух членов класса). Затем строка копируется из string_view. Ничего сложного.



                Теперь возьмем тот же код, но при этом run будет принимать ссылку на string.



                void run(string const & v)
                {
                other(v);
                }

                void general(string str)
                {
                other(str);
                //str используется, её нельзя перемещать в other
                }


                Да, Вы правы, передача ссылки в функцию run - быстрая операция, можно считать, что это копирование указателя. Но у передачи ссылки есть и минусы. Один из них - функция run обращается к строке через ссылку, т.е. появляется дополнительный уровень косвенности, который может вылиться в большие затраты, чем "лишнее" копирование указателя в версии функции, принимающей string_view.



                Теперь рассмотрим третий вариант функции run:



                void run(string v)
                {
                other(std::move(v));
                }


                В данном случае копирование строки происходит еще в функции general (причем мы не перемещаем параметр str, т.к. он где-то там еще нужен далее). Затем функция run перемещает объект v в параметр функции other, а это в нашем случае приводит к копированию трех указателей и занулению старых. Очевидно, что в этом случае операций намного больше, чем со string_view, и, вполне вероятно, что косвенное обращение будет тоже быстрее. То есть это может быть самым тормознутым вариантом.



                А теперь представим, такую ситуацию:



                void run(string const &); //<-- имеется такая run
                void run(string_view); //или такая, нам без разницы

                class SomeClass
                {
                string m_text;
                mutex m_mutex;
                void SomeClass::call()
                {
                //Мы знаем, что run делает копию строки,
                //и запускает поток для её обработки
                lock_quard<mutex> locker;//объект у нас защищен мьютексом
                run(m_text);//Где-то там строка копируется и запускается новый поток
                //мы не знаем точный момент, когда строка скопируется,
                //поэтому вынуждены ждать выполнения run под защитой мьютекса,
                //т.к. в other содержаться указатели на защищенные члены нашего класса.
                //Вполне вероятно, что в этот момент другие
                //потоки уже хотят работать с нашим объектом,
                //но не могут, т.к. мьютекс захвачен.
                }
                };


                Я в комментариях описал проблему. А теперь возьмем ситуацию с копированием и перемещением:



                void run(string); //<-- теперь run принимает копию

                class SomeClass
                {
                //..
                void SomeClass::call()
                {
                //Мы знаем, что run принимает копию строки
                //и запускает поток для её обработки
                unique_lock<mutex> locker;//объект у нас защищен мьютексом
                string text_copy = m_text;//Под защитой выполняем копирование строки
                locker.unlock();//И разблокируем мьютекс, т.к.
                run(std::move(text_copy));//нам уже без разницы что-том делает run,
                //копия данных для него уже создана и другим потокам можно дать доступ к объекту
                }
                };


                У string_view при этом тоже имеются свои прелести. string_view в принципе не привязан к string - это просто данные и их размер. Т.е. string_view может работать не только с string:



                void run(string_view v);

                void general(string str)
                {
                other(str);//ok
                }

                void general(char const * str, size_t len)
                {
                other(string_view(str, len));//ok
                }

                void general(char const * str)
                {
                other(string_view(str));//ok
                }

                void general(std::vector<char> const & v)
                {
                other(string_view(v.data(), v.size()));//ok
                }


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



                Но оптимизации также могут перевернуть всё с ног на голову. Когда нужно сделать копию строки, кажется, что это приведет к аллокации памяти и всё это будет медленно. В общем случае это верно, но есть такая штука, как SSO (small string optimization), которая позволяет хранить маленькие строки прямо в объекте string, используя сам объект как буфер для строки. В случае применения такой оптимизации копирование может оказаться равноценно или даже дешевле перемещения.



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




                а для чего тогда стоит использовать string_view?




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



                Всё сказанное выше весьма условно и служит только для демонстрации различных ситуаций.



                Как видите, применение того или иного средства полностью зависит от конкретных условий использования, применяемых алгоритмов, оптимизаций компилятора, устройства библиотеки, платформы на которой всё это работает и т.д., поэтому закончу тем, с чего начал - использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий.






                поделиться|улучшить этот ответ




























                  1












                  1








                  1







                  Использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий. И это относится не только к std::string и std::string_view



                  Для начала определимся с реализацией наших сущностей. Пусть std::string построен на трех указателях по 4 байта каждый (или указатель и два размера):



                  class string
                  {
                  //...
                  pointer * m_begin; //Начало строки
                  pointer * m_end; //Указатель за последний элемент
                  pointer * m_end_of_storage; //Указатель за конец выделенной памяти
                  };


                  std::string_view при этом реализован с помощью двух указателей (или указатель и размер):



                  class string_view
                  {
                  //...
                  pointer m_data; //Указатель на начало данных
                  pointer m_end; //Указатель за конец данных
                  };


                  Упрощенно рассмотрим три версии простого кода:



                  void other(std::string); //<-- функция, в которую всегда передается копия.

                  void run(string_view v) //<-- Функция, которая вызывает функцию other
                  {
                  //т.е. эта функция делает ту самую копию объекта
                  other(v);
                  }

                  void general(string str)
                  {
                  //А из этой функции в функцию run передается строка
                  other(str);
                  }


                  Что будет сгенерировано компилятором и как рассматривать не будем, т.к. это сильно зависит от компилятора и параметров компиляции, но постараемся посмотреть что может быть и как.



                  В нашем случае other передает копию string_view, т.е. general копирует два указателя (не говорим о возможности вызова конструктора копирования, подразумевая, что он самый тривиальный и в результирующем коде приведет просто к копированию двух членов класса). Затем строка копируется из string_view. Ничего сложного.



                  Теперь возьмем тот же код, но при этом run будет принимать ссылку на string.



                  void run(string const & v)
                  {
                  other(v);
                  }

                  void general(string str)
                  {
                  other(str);
                  //str используется, её нельзя перемещать в other
                  }


                  Да, Вы правы, передача ссылки в функцию run - быстрая операция, можно считать, что это копирование указателя. Но у передачи ссылки есть и минусы. Один из них - функция run обращается к строке через ссылку, т.е. появляется дополнительный уровень косвенности, который может вылиться в большие затраты, чем "лишнее" копирование указателя в версии функции, принимающей string_view.



                  Теперь рассмотрим третий вариант функции run:



                  void run(string v)
                  {
                  other(std::move(v));
                  }


                  В данном случае копирование строки происходит еще в функции general (причем мы не перемещаем параметр str, т.к. он где-то там еще нужен далее). Затем функция run перемещает объект v в параметр функции other, а это в нашем случае приводит к копированию трех указателей и занулению старых. Очевидно, что в этом случае операций намного больше, чем со string_view, и, вполне вероятно, что косвенное обращение будет тоже быстрее. То есть это может быть самым тормознутым вариантом.



                  А теперь представим, такую ситуацию:



                  void run(string const &); //<-- имеется такая run
                  void run(string_view); //или такая, нам без разницы

                  class SomeClass
                  {
                  string m_text;
                  mutex m_mutex;
                  void SomeClass::call()
                  {
                  //Мы знаем, что run делает копию строки,
                  //и запускает поток для её обработки
                  lock_quard<mutex> locker;//объект у нас защищен мьютексом
                  run(m_text);//Где-то там строка копируется и запускается новый поток
                  //мы не знаем точный момент, когда строка скопируется,
                  //поэтому вынуждены ждать выполнения run под защитой мьютекса,
                  //т.к. в other содержаться указатели на защищенные члены нашего класса.
                  //Вполне вероятно, что в этот момент другие
                  //потоки уже хотят работать с нашим объектом,
                  //но не могут, т.к. мьютекс захвачен.
                  }
                  };


                  Я в комментариях описал проблему. А теперь возьмем ситуацию с копированием и перемещением:



                  void run(string); //<-- теперь run принимает копию

                  class SomeClass
                  {
                  //..
                  void SomeClass::call()
                  {
                  //Мы знаем, что run принимает копию строки
                  //и запускает поток для её обработки
                  unique_lock<mutex> locker;//объект у нас защищен мьютексом
                  string text_copy = m_text;//Под защитой выполняем копирование строки
                  locker.unlock();//И разблокируем мьютекс, т.к.
                  run(std::move(text_copy));//нам уже без разницы что-том делает run,
                  //копия данных для него уже создана и другим потокам можно дать доступ к объекту
                  }
                  };


                  У string_view при этом тоже имеются свои прелести. string_view в принципе не привязан к string - это просто данные и их размер. Т.е. string_view может работать не только с string:



                  void run(string_view v);

                  void general(string str)
                  {
                  other(str);//ok
                  }

                  void general(char const * str, size_t len)
                  {
                  other(string_view(str, len));//ok
                  }

                  void general(char const * str)
                  {
                  other(string_view(str));//ok
                  }

                  void general(std::vector<char> const & v)
                  {
                  other(string_view(v.data(), v.size()));//ok
                  }


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



                  Но оптимизации также могут перевернуть всё с ног на голову. Когда нужно сделать копию строки, кажется, что это приведет к аллокации памяти и всё это будет медленно. В общем случае это верно, но есть такая штука, как SSO (small string optimization), которая позволяет хранить маленькие строки прямо в объекте string, используя сам объект как буфер для строки. В случае применения такой оптимизации копирование может оказаться равноценно или даже дешевле перемещения.



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




                  а для чего тогда стоит использовать string_view?




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



                  Всё сказанное выше весьма условно и служит только для демонстрации различных ситуаций.



                  Как видите, применение того или иного средства полностью зависит от конкретных условий использования, применяемых алгоритмов, оптимизаций компилятора, устройства библиотеки, платформы на которой всё это работает и т.д., поэтому закончу тем, с чего начал - использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий.






                  поделиться|улучшить этот ответ















                  Использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий. И это относится не только к std::string и std::string_view



                  Для начала определимся с реализацией наших сущностей. Пусть std::string построен на трех указателях по 4 байта каждый (или указатель и два размера):



                  class string
                  {
                  //...
                  pointer * m_begin; //Начало строки
                  pointer * m_end; //Указатель за последний элемент
                  pointer * m_end_of_storage; //Указатель за конец выделенной памяти
                  };


                  std::string_view при этом реализован с помощью двух указателей (или указатель и размер):



                  class string_view
                  {
                  //...
                  pointer m_data; //Указатель на начало данных
                  pointer m_end; //Указатель за конец данных
                  };


                  Упрощенно рассмотрим три версии простого кода:



                  void other(std::string); //<-- функция, в которую всегда передается копия.

                  void run(string_view v) //<-- Функция, которая вызывает функцию other
                  {
                  //т.е. эта функция делает ту самую копию объекта
                  other(v);
                  }

                  void general(string str)
                  {
                  //А из этой функции в функцию run передается строка
                  other(str);
                  }


                  Что будет сгенерировано компилятором и как рассматривать не будем, т.к. это сильно зависит от компилятора и параметров компиляции, но постараемся посмотреть что может быть и как.



                  В нашем случае other передает копию string_view, т.е. general копирует два указателя (не говорим о возможности вызова конструктора копирования, подразумевая, что он самый тривиальный и в результирующем коде приведет просто к копированию двух членов класса). Затем строка копируется из string_view. Ничего сложного.



                  Теперь возьмем тот же код, но при этом run будет принимать ссылку на string.



                  void run(string const & v)
                  {
                  other(v);
                  }

                  void general(string str)
                  {
                  other(str);
                  //str используется, её нельзя перемещать в other
                  }


                  Да, Вы правы, передача ссылки в функцию run - быстрая операция, можно считать, что это копирование указателя. Но у передачи ссылки есть и минусы. Один из них - функция run обращается к строке через ссылку, т.е. появляется дополнительный уровень косвенности, который может вылиться в большие затраты, чем "лишнее" копирование указателя в версии функции, принимающей string_view.



                  Теперь рассмотрим третий вариант функции run:



                  void run(string v)
                  {
                  other(std::move(v));
                  }


                  В данном случае копирование строки происходит еще в функции general (причем мы не перемещаем параметр str, т.к. он где-то там еще нужен далее). Затем функция run перемещает объект v в параметр функции other, а это в нашем случае приводит к копированию трех указателей и занулению старых. Очевидно, что в этом случае операций намного больше, чем со string_view, и, вполне вероятно, что косвенное обращение будет тоже быстрее. То есть это может быть самым тормознутым вариантом.



                  А теперь представим, такую ситуацию:



                  void run(string const &); //<-- имеется такая run
                  void run(string_view); //или такая, нам без разницы

                  class SomeClass
                  {
                  string m_text;
                  mutex m_mutex;
                  void SomeClass::call()
                  {
                  //Мы знаем, что run делает копию строки,
                  //и запускает поток для её обработки
                  lock_quard<mutex> locker;//объект у нас защищен мьютексом
                  run(m_text);//Где-то там строка копируется и запускается новый поток
                  //мы не знаем точный момент, когда строка скопируется,
                  //поэтому вынуждены ждать выполнения run под защитой мьютекса,
                  //т.к. в other содержаться указатели на защищенные члены нашего класса.
                  //Вполне вероятно, что в этот момент другие
                  //потоки уже хотят работать с нашим объектом,
                  //но не могут, т.к. мьютекс захвачен.
                  }
                  };


                  Я в комментариях описал проблему. А теперь возьмем ситуацию с копированием и перемещением:



                  void run(string); //<-- теперь run принимает копию

                  class SomeClass
                  {
                  //..
                  void SomeClass::call()
                  {
                  //Мы знаем, что run принимает копию строки
                  //и запускает поток для её обработки
                  unique_lock<mutex> locker;//объект у нас защищен мьютексом
                  string text_copy = m_text;//Под защитой выполняем копирование строки
                  locker.unlock();//И разблокируем мьютекс, т.к.
                  run(std::move(text_copy));//нам уже без разницы что-том делает run,
                  //копия данных для него уже создана и другим потокам можно дать доступ к объекту
                  }
                  };


                  У string_view при этом тоже имеются свои прелести. string_view в принципе не привязан к string - это просто данные и их размер. Т.е. string_view может работать не только с string:



                  void run(string_view v);

                  void general(string str)
                  {
                  other(str);//ok
                  }

                  void general(char const * str, size_t len)
                  {
                  other(string_view(str, len));//ok
                  }

                  void general(char const * str)
                  {
                  other(string_view(str));//ok
                  }

                  void general(std::vector<char> const & v)
                  {
                  other(string_view(v.data(), v.size()));//ok
                  }


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



                  Но оптимизации также могут перевернуть всё с ног на голову. Когда нужно сделать копию строки, кажется, что это приведет к аллокации памяти и всё это будет медленно. В общем случае это верно, но есть такая штука, как SSO (small string optimization), которая позволяет хранить маленькие строки прямо в объекте string, используя сам объект как буфер для строки. В случае применения такой оптимизации копирование может оказаться равноценно или даже дешевле перемещения.



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




                  а для чего тогда стоит использовать string_view?




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



                  Всё сказанное выше весьма условно и служит только для демонстрации различных ситуаций.



                  Как видите, применение того или иного средства полностью зависит от конкретных условий использования, применяемых алгоритмов, оптимизаций компилятора, устройства библиотеки, платформы на которой всё это работает и т.д., поэтому закончу тем, с чего начал - использование любого из альтернативных средств очень сильно зависит от ситуации, и выбирается исходя из конкретных условий.







                  поделиться|улучшить этот ответ














                  поделиться|улучшить этот ответ



                  поделиться|улучшить этот ответ








                  изменён 9 часов назад

























                  ответ дан 10 часов назад









                  CroessmahCroessmah

                  3,973717




                  3,973717

























                      2














                      Просто std::string. А потом вместо компирования - перемещать через std::move.






                      поделиться|улучшить этот ответ
























                      • а для чего тогда стоит использовать string_view? И чем это лучше чем константная ссылка, с ней то по идее будет вызван только copy конструктор

                        – tim bars
                        12 часов назад













                      • это всё весьма и весьма сомнительно.

                        – Croessmah
                        12 часов назад











                      • @timbars "чем это лучше" Тем, что при таком варианте мы можем в некоторых случаях избежать копирования - когда передаем в функцию временный объект (rvalue). string_view, имхо, нужен для функций, анализирующих строки или сегменты строк (при парсинге и т. п.). Как универсальный "параметр-строка" он не особо прижился, имхо, потому что часто требудется, чтобы параметр был null-terminated, чего string_view не дает.

                        – HolyBlackCat
                        12 часов назад













                      • @Croessmah Расска́жите подробнее?

                        – HolyBlackCat
                        12 часов назад
















                      2














                      Просто std::string. А потом вместо компирования - перемещать через std::move.






                      поделиться|улучшить этот ответ
























                      • а для чего тогда стоит использовать string_view? И чем это лучше чем константная ссылка, с ней то по идее будет вызван только copy конструктор

                        – tim bars
                        12 часов назад













                      • это всё весьма и весьма сомнительно.

                        – Croessmah
                        12 часов назад











                      • @timbars "чем это лучше" Тем, что при таком варианте мы можем в некоторых случаях избежать копирования - когда передаем в функцию временный объект (rvalue). string_view, имхо, нужен для функций, анализирующих строки или сегменты строк (при парсинге и т. п.). Как универсальный "параметр-строка" он не особо прижился, имхо, потому что часто требудется, чтобы параметр был null-terminated, чего string_view не дает.

                        – HolyBlackCat
                        12 часов назад













                      • @Croessmah Расска́жите подробнее?

                        – HolyBlackCat
                        12 часов назад














                      2












                      2








                      2







                      Просто std::string. А потом вместо компирования - перемещать через std::move.






                      поделиться|улучшить этот ответ













                      Просто std::string. А потом вместо компирования - перемещать через std::move.







                      поделиться|улучшить этот ответ












                      поделиться|улучшить этот ответ



                      поделиться|улучшить этот ответ










                      ответ дан 12 часов назад









                      HolyBlackCatHolyBlackCat

                      5,6041514




                      5,6041514













                      • а для чего тогда стоит использовать string_view? И чем это лучше чем константная ссылка, с ней то по идее будет вызван только copy конструктор

                        – tim bars
                        12 часов назад













                      • это всё весьма и весьма сомнительно.

                        – Croessmah
                        12 часов назад











                      • @timbars "чем это лучше" Тем, что при таком варианте мы можем в некоторых случаях избежать копирования - когда передаем в функцию временный объект (rvalue). string_view, имхо, нужен для функций, анализирующих строки или сегменты строк (при парсинге и т. п.). Как универсальный "параметр-строка" он не особо прижился, имхо, потому что часто требудется, чтобы параметр был null-terminated, чего string_view не дает.

                        – HolyBlackCat
                        12 часов назад













                      • @Croessmah Расска́жите подробнее?

                        – HolyBlackCat
                        12 часов назад



















                      • а для чего тогда стоит использовать string_view? И чем это лучше чем константная ссылка, с ней то по идее будет вызван только copy конструктор

                        – tim bars
                        12 часов назад













                      • это всё весьма и весьма сомнительно.

                        – Croessmah
                        12 часов назад











                      • @timbars "чем это лучше" Тем, что при таком варианте мы можем в некоторых случаях избежать копирования - когда передаем в функцию временный объект (rvalue). string_view, имхо, нужен для функций, анализирующих строки или сегменты строк (при парсинге и т. п.). Как универсальный "параметр-строка" он не особо прижился, имхо, потому что часто требудется, чтобы параметр был null-terminated, чего string_view не дает.

                        – HolyBlackCat
                        12 часов назад













                      • @Croessmah Расска́жите подробнее?

                        – HolyBlackCat
                        12 часов назад

















                      а для чего тогда стоит использовать string_view? И чем это лучше чем константная ссылка, с ней то по идее будет вызван только copy конструктор

                      – tim bars
                      12 часов назад







                      а для чего тогда стоит использовать string_view? И чем это лучше чем константная ссылка, с ней то по идее будет вызван только copy конструктор

                      – tim bars
                      12 часов назад















                      это всё весьма и весьма сомнительно.

                      – Croessmah
                      12 часов назад





                      это всё весьма и весьма сомнительно.

                      – Croessmah
                      12 часов назад













                      @timbars "чем это лучше" Тем, что при таком варианте мы можем в некоторых случаях избежать копирования - когда передаем в функцию временный объект (rvalue). string_view, имхо, нужен для функций, анализирующих строки или сегменты строк (при парсинге и т. п.). Как универсальный "параметр-строка" он не особо прижился, имхо, потому что часто требудется, чтобы параметр был null-terminated, чего string_view не дает.

                      – HolyBlackCat
                      12 часов назад







                      @timbars "чем это лучше" Тем, что при таком варианте мы можем в некоторых случаях избежать копирования - когда передаем в функцию временный объект (rvalue). string_view, имхо, нужен для функций, анализирующих строки или сегменты строк (при парсинге и т. п.). Как универсальный "параметр-строка" он не особо прижился, имхо, потому что часто требудется, чтобы параметр был null-terminated, чего string_view не дает.

                      – HolyBlackCat
                      12 часов назад















                      @Croessmah Расска́жите подробнее?

                      – HolyBlackCat
                      12 часов назад





                      @Croessmah Расска́жите подробнее?

                      – HolyBlackCat
                      12 часов назад











                      1
















                      1. Если строка будет "просто скопирована", то вам следует реализовывать либо "ленивый" вариант семантики перемещения (передавать std::string по значению и затем делать из него перемещение), либо "полный" вариант семантики перемещения (писать две перегруженных функции: для const std::string & и для std::string &&), либо, возможно, реализовать forwarding (писать шаблонную функцию, принимающую универсальную сслыку и делающую std::forward в вашу копию).



                        См. https://ru.stackoverflow.com/a/822789/182825



                      2. std::string_view уместен везде, где вы будете просто анализировать строку, т.е. он является заменителем const std:string & в ситуациях, когда копирование не будет делаться.







                      поделиться|улучшить этот ответ




























                        1
















                        1. Если строка будет "просто скопирована", то вам следует реализовывать либо "ленивый" вариант семантики перемещения (передавать std::string по значению и затем делать из него перемещение), либо "полный" вариант семантики перемещения (писать две перегруженных функции: для const std::string & и для std::string &&), либо, возможно, реализовать forwarding (писать шаблонную функцию, принимающую универсальную сслыку и делающую std::forward в вашу копию).



                          См. https://ru.stackoverflow.com/a/822789/182825



                        2. std::string_view уместен везде, где вы будете просто анализировать строку, т.е. он является заменителем const std:string & в ситуациях, когда копирование не будет делаться.







                        поделиться|улучшить этот ответ


























                          1












                          1








                          1









                          1. Если строка будет "просто скопирована", то вам следует реализовывать либо "ленивый" вариант семантики перемещения (передавать std::string по значению и затем делать из него перемещение), либо "полный" вариант семантики перемещения (писать две перегруженных функции: для const std::string & и для std::string &&), либо, возможно, реализовать forwarding (писать шаблонную функцию, принимающую универсальную сслыку и делающую std::forward в вашу копию).



                            См. https://ru.stackoverflow.com/a/822789/182825



                          2. std::string_view уместен везде, где вы будете просто анализировать строку, т.е. он является заменителем const std:string & в ситуациях, когда копирование не будет делаться.







                          поделиться|улучшить этот ответ















                          1. Если строка будет "просто скопирована", то вам следует реализовывать либо "ленивый" вариант семантики перемещения (передавать std::string по значению и затем делать из него перемещение), либо "полный" вариант семантики перемещения (писать две перегруженных функции: для const std::string & и для std::string &&), либо, возможно, реализовать forwarding (писать шаблонную функцию, принимающую универсальную сслыку и делающую std::forward в вашу копию).



                            См. https://ru.stackoverflow.com/a/822789/182825



                          2. std::string_view уместен везде, где вы будете просто анализировать строку, т.е. он является заменителем const std:string & в ситуациях, когда копирование не будет делаться.








                          поделиться|улучшить этот ответ












                          поделиться|улучшить этот ответ



                          поделиться|улучшить этот ответ










                          ответ дан 9 часов назад









                          AnTAnT

                          50.2k33895




                          50.2k33895






























                              черновик сохранён

                              черновик удалён




















































                              Спасибо за ваш ответ на Stack Overflow на русском!


                              • Пожалуйста, убедитесь, что публикуемое сообщение отвечает на поставленный вопрос. Предоставьте как можно больше деталей, расскажите про проведенное исследование!

                              Но избегайте



                              • Просьб помощи, уточнений или ответов на темы не относящиеся к вопросу.

                              • Ответов основанных на мнениях; приводите аргументы основанные только на реальном опыте.


                              Также, обратите внимание на заметку в справочном центре о том, как писать ответы.




                              черновик сохранён


                              черновик удалён














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fru.stackoverflow.com%2fquestions%2f954084%2fstdstring-vs-const-stdstring-vs-stdstring-view%23new-answer', 'question_page');
                              }
                              );

                              Отправить без регистрации















                              Необходима, но никому не показывается





















































                              Необходима, но никому не показывается














                              Необходима, но никому не показывается












                              Необходима, но никому не показывается







                              Необходима, но никому не показывается

































                              Необходима, но никому не показывается














                              Необходима, но никому не показывается












                              Необходима, но никому не показывается







                              Необходима, но никому не показывается







                              Popular posts from this blog

                              Couldn't open a raw socket. Error: Permission denied (13) (nmap)Is it possible to run networking commands...

                              VNC viewer RFB protocol error: bad desktop size 0x0I Cannot Type the Key 'd' (lowercase) in VNC Viewer...

                              Why not use the yoke to control yaw, as well as pitch and roll? Announcing the arrival of...