Стоит ли сравнивать 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).

Хотя выделение или нет - это деталь реализации, не представляется разумным, чтобы сравнение последовательностей делало это.