На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Передача строки в качестве параметра
    ExpandedWrap disabled
      #include <iostream>
      #include <string>
       
      void test(const std::string& str)
      {
          std::cout << str;
      }
       
      int main()
      {
        test("Hello my dear friend!\n");
      }

    Что происходит?
    Создаётся временный объект const std::string, конструктор которого принимает (и копирует в себя) const char*, вызывается функция test, затем объект уничтожается. Много лишних действий.

    Можно ли сделать так, чтобы в EXE-шнике изначально хранился объект std::string, а не создавался перед вызовом функции?

    p.s. В идеале – C++11, но если вариантов нет, рассмотрю что есть.

    p.p.s. Интересен также вариант без создания промежуточных констант, если такое возможно, потому что, понятное, дело, можно сделать const std::string text = "Hello my dear friend!\n"; и подставлять её, но это неинтересно. Да и всё равно объект создаётся динамически.
      Цитата Jin X @
      Что происходит?
      ...
      Почти. Создаётся временный std::string (r-value), который успешно биндится на константную l-value ссылку, и чьё время жизни заканчивается по достижению ;, т.е. по возврату из test().
      Цитата Jin X @
      Можно ли сделать так, чтобы в EXE-шнике изначально хранился объект std::string, а не создавался перед вызовом функции?
      Нет. Объекты с нетривиальными конструкторами не могут быть созданы иначе, нежели в run-time, кроме как constexpr. Конкретно std::string требует наличия хипа для связи указателей с хранилищем dynamic storage, поэтому не может иметь constexpr конструкторов.
        Qraizer, я тут делаю одну штуку, в которой путём экспериментов мне удалось сделать функцию constexpr std::string. Прога компилится в GCC, VS и ICC, а вот Clang на неё ругается.
        Правда, когда я пытаюсь выдернуть этот кусок отдельно, то ничего не получается, компилеры ругаются как раз на constexpr std::string.

        Можешь глянуть и объяснить это явление?
        Ищи static constexpr std::string get_default_suffix_text()

        p.s. Эта штука не доделана, поэтому выкладываю под названием temp :)
        [прикреплённый файл удалён]

        Добавлено
        Навели на мысль заюзать string_view.
        Выглядите куда веселее: https://godbolt.org/z/XAm-S-
        Жаль только, что C++17.
        Сообщение отредактировано: Jin X -
          Цитата Jin X @
          Можешь глянуть и объяснить это явление?
          constexpr для функций означает лишь, что они могут использоваться в константных выражениях. Исполнение их в компайл-тайм может оказаться невозможным, но компилятор может сделать всё возможное, чтобы всё возможное выполнить при компиляции. Ошибкой будет считаться лишь невозможность породить результат т.о., чтобы в используемом контексте его можно было бы использовать. Например, если результат constexpr функции, возвращающей int, будет использовать как указание размера массива в его определении, ибо Стандарт обязывает указывать там только константные выражения, вычислимые в компайл-тайм. Так что формально в твоём случае компилятор может выполнить всю работу get_default_suffix_text(), но отложить создание результирующего объекта до ран-тайм, т.к. контекст не обязывает его предоставить результат ранее ран-тайма. Насколько мне известно, но это неточно, в C++20 потребовали, чтобы constexpr стало обязательной инструкцией компилятору. Вероятно поэтому clang и ругается. Однако C++20 пока ещё не ратифицирован. Более того, в описании constexpr среди прочего указано, что возвращаемое значение должно быть literal type, к каковым std::string явно не относится, т.к. literal type должен иметь (по меньшей мере, но не ограничиваясь) тривиальный деструктор.
          Цитата Jin X @
          Навели на мысль заюзать string_view.
          std::string_view не хранит информации, он лишь ссылается на неё, хранимую ещё где-то. В твоём случае строковый литерал вполне подходит, т.к. имеет время жизни до конца программы (static storage duration). Но в общем случае это противоречивый класс: являясь по сути ссылочным типом, легко впоследствии может оказаться указываемым в ничто, если исходный объект будет разрушен раньше времени. Зато string_view позволяет работать с собой как с constexpr.
          Сообщение отредактировано: Qraizer -
            Цитата Jin X @
            можно сделать const std::string text = "Hello my dear friend!\n"; и подставлять её, но это неинтересно. Да и всё равно объект создаётся динамически.

            А можно сделать static const std::string и объект будет создаваться только 1 раз (если цель в экономии тактов процессора).
            Сообщение отредактировано: shm -
              Спасибо, Qraizer за разъяснения ;)
              Да, я уже понял, что Clang более чётко следует стандартам.

              Цитата Qraizer @
              являясь по сути ссылочным типом, легко впоследствии может оказаться указываемым в ничто, если исходный объект будет разрушен раньше времени.
              Да, я понимаю, что при передаче string это будет просто size() и c_str(), а при строковом литерале — его длина и const char* со всеми вытекающими. Так-то, и при передаче const string& значение параметра может поменяться, если исходный объект будет изменён раньше времени (а если это будет динамически созданный объект, то так же может указывать в никуда). И такую строку тоже нужно копировать во избежание этого.


              shm, это я понимаю. Но я думал, что можно в копмайл-тайм создать его, ан нет. Просто, если функция вызывается много раз с разными константными строками, всё равно их все придётся создавать динамически. И даже копировать.
              Не то, чтоб я прям хотел сэкономить такты, просто бессмысленность вот этого копирования (а еще ведь надо память выделить и освободить) и пр. смутила :). Что-то компилятор, конечно, соптимизирует, но это все implementation defined, как говорится.
                Тема раскрыта, например, тут. Но я думаю, что все это преждевременная оптимизация.
                  Цитата shm @
                  Тема раскрыта, например, тут. Но я думаю, что все это преждевременная оптимизация.
                  Ну да, любопытно. Но заморочи много. Действительно, не всегда это надо.

                  Цитата Qraizer @
                  Насколько мне известно, но это неточно, в C++20 потребовали, чтобы constexpr стало обязательной инструкцией компилятору.
                  Инструкцией к чему?
                  Для обязательного выполнения именно в compile time вводится consteval.

                  Добавлено
                  Кстати, что интересно, код со string_view более оптимальный даже, чем с char*, ибо там уже есть длина строки (странно только, что этот код не инлайнится автоматом): https://godbolt.org/z/oJokK8 :)
                    Цитата Jin X @
                    Можно ли сделать так, чтобы в EXE-шнике изначально хранился объект std::string, а не создавался перед вызовом функции?
                    Можно. А как Вы собиратесь привязать его к конкретному вызову конкретной функции. Хорошо, когда функция простейшая и вызывается из одного потока. Тогда до завершения одной её ветви следующий вызов не поступит. А если приложение многопоточное? Многопоточность есть многозадачность не ситемы, а отдельно взятой исполняемой под системой программы. Поток прервался посреди функции, другой поток вызвал её ещё раз с другим фактическим параметром, получили две ветви одной функции, каждая вызвана из своего потока, потом первая ветвь продолжила выполняться, а строка уже другая. Или функция рекурсивна. Такая функция, не завершившись, вызывает себя с другим фактическим параметром. Завершаясь же, такая функция возвращает управление себе. Вызвала функция себя, строка изменилась, потом функция заврешилась и вернула управление той себе, которая вызвана не из себя, а из мэйн, там строка должна быть из первого вызова, а будет из второго. К тому же копирование и удаление даже в самом кривом исполнении занимают меньше тактов, чем оптимальное копирование. Если только это действительно копирование, а не асоциирование. То есть если после
                    ExpandedWrap disabled
                      char s[]="какой-то текст";
                      t(s);
                    символы будут в двух экземплярах. Но если ты точно знаешь, что таких накладок не будет, то, в принципе, можно создать строку заранее в любом месте вызывающей функции, её модуля, или глобально, все операции, модифицирующие значение фактического параметра, выполнять прямо над самой строкой, а в функцию передавать ссылку или указатель. Только когда строк окажется всё же две разные, придётся самому позаботиться о каждой.

                    Добавлено
                    Цитата Qraizer @
                    Нет. Объекты с нетривиальными конструкторами не могут быть созданы иначе, нежели в run-time, кроме как constexpr.
                    В рантайм? Или перед самым вызовом конкретной функции? Вот в чём вопрос. Заранее создать можно, статически нельзя.
                      Цитата Ирокез @
                      Заранее создать можно, статически нельзя.
                      Вопрос как раз в создании статически.
                      Строка константная, изменена не будет, поэтому проблем с многопоточностью нет.
                        Цитата Jin X @
                        Вопрос как раз в создании статически.
                        Нельзя. Да и зачем? Вы ж разменяете быстродействие процессора на вращение диска и можете даже потерять.
                        Цитата Jin X @
                        Строка константная, изменена не будет, поэтому проблем с многопоточностью нет.
                        А компилятор об этом знает? Даже если он способен проанализировать весь исходник, то и тогда не сможет сделать вывод о том, что других строк не появится. Может строка ровно одна пока? Компилятор этого исключить не может, а авторы линкера и кодогенератора вообще не в курсе количества строк. И если строка одна, то её так и так придётся сначала создать, а потом удалить. Вот если строк миллиард, но никакие две не нужны одновременно, тогда можно, разместив их в одном и том же объекте, а меняя только его значение, сэкономить на девятьсот девяносто миллионов девятьсот девяносто девять тысяч девятьсот девяносто девяти вызовах конструктора и таком же количестве вызовов деструктора. Да и то надо ещё постараться, чтоб те же вызовы не «влезли через окно». Да и даже если бы можно было бы сэкономить единственный вызов конструктора, пользователь этого не заметит даже если он вообще не из нашей метагалактики.
                          Цитата Jin X @
                          Строка константная, изменена не будет
                          Создай статически, объяви константной. Если в библиотеке предусмотрено константное поведение строк, от части операций удастся избавиться. Однако перестанут вызываться функции, в которые эта строка передаётся по неконстантной ссылке. Так что константной объявить строку возможно и не получится (зато узнаешь, как часто эта строка передаётся в функции, которые её содержимое, по мнению компилятора, могут изменить, если не сейчас, то когда-нибудь в будущем, если будут переписаны).
                          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                          0 пользователей:


                          Рейтинг@Mail.ru
                          [ Script execution time: 0,0368 ]   [ 16 queries used ]   [ Generated: 16.04.24, 16:45 GMT ]