Стоит ли сравнивать std :: string с "string" или "string"?
Рассмотрим этот фрагмент кода:
bool foo(const std::string& s) {
return s == "hello"; // comparing against a const char* literal
}
bool bar(const std::string& s) {
return s == "hello"s; // comparing against a std::string literal
}
На первый взгляд, похоже, что для сравнения с const char*
требуется меньше ассемблерных инструкций 1 так как использование строкового литерала приведет к созданию std::string
.
(РЕДАКТИРОВАТЬ: как указывалось в ответах, я забыл о том факте, что s.compare(const char*)
будет эффективно вызываться в foo()
, поэтому, конечно, в этом случае не происходит никакого создания на месте. несколько строк ниже.)
Однако, глядя на operator==(const char*, const std::string&)
ссылка:
Все сравнения выполняются через функцию-член compare()
.
Насколько я понимаю, это означает, что нам все равно нужно будет создать std::string
для того, чтобы выполнить сравнение, поэтому я подозреваю, что издержки в конце будут такими же (хотя они и скрываются при вызове operator==
).
- Какие из сравнений я должен предпочесть?
- Есть ли у одной версии преимущества перед другой (может быть в определенных ситуациях)?
1 Я знаю, что меньшее количество инструкций по сборке не обязательно означает более быстрый код, но я не хочу вдаваться в микропроцессорность здесь.
Ответы
Ответ 1
Ни.
Если вы хотите быть умным, сравните со "string"sv
, который возвращает std::string_view
.
Хотя сравнение с литералом типа "string"
не приводит к каким-либо накладным расходам на выделение ресурсов, оно рассматривается как строка с нулевым символом в конце со всеми вытекающими из этого недостатками: нет допусков для встроенных нулей, и пользователи должны учитывать нулевой терминатор.
"string"s
выполняет выделение, исключая оптимизацию small-string или выделение выделения. Кроме того, оператору передается длина литерала, не нужно считать, и он учитывает встроенные нули.
И, наконец, использование "string"sv
сочетает в себе преимущества обоих других подходов, избегая их индивидуальных недостатков. Кроме того, std::string_view
- намного более простой зверь, чем std::string
, особенно если последний использует SSO, как и все современные.
По крайней мере, начиная с С++ 14 (который обычно разрешал исключать выделения), компиляторы теоретически могли оптимизировать все параметры до последнего, учитывая достаточную информацию (обычно доступную для примера) и усилия, по правилу " как будто". Мы еще не там, хотя.
Ответ 2
Нет, compare()
не требует создания std::string
для операндов const char*
.
Вы используете перегрузку № 4 здесь.
Сравнение со строковым литералом - это "бесплатная" версия, которую вы ищете. Создание std::string
здесь совершенно не нужно.
Ответ 3
Насколько я понимаю, это означает, что нам все равно нужно будет создать std::string
для того, чтобы выполнить сравнение, поэтому я подозреваю, что издержки в конце будут такими же (хотя они и скрываются при вызове operator==
).
Вот где это рассуждение идет не так. std::compare
не нужно выделять свой операнд в виде строки с нулевым символом в конце для функции. По одной из перегрузок:
int compare( const CharT* s ) const; // (4)
4) Сравнивает эту строку с символьной последовательностью с нулевым символом в конце, начинающейся с символа, на который указывает s
с длиной Traits::length(s)
.
Хотя выделение или нет - это деталь реализации, не представляется разумным, чтобы сравнение последовательностей делало это.