Почему методы std :: string, работающие с std :: string_view, обязаны преобразовывать аргумент своего шаблона в string_view в своем теле - PullRequest
0 голосов
/ 23 февраля 2020

В std::basic_string таких методах, как append, которые работают с std::basic_string_view, стандарт в основном требует 3 вещей (https://en.cppreference.com/w/cpp/string/basic_string/append):

template basic_string & append (const T & t);

  1. Неявно преобразует t в строковое представление sv как будто с помощью std::basic_string_view<CharT, Traits> sv = t;
  2. , а затем добавляет все символы из sv как будто с помощью append(sv.data(), sv.size()).
  3. Эта перегрузка участвует в разрешении перегрузки, только если std::is_convertible_v<const T&, std::basic_string_view<CharT, Traits>> имеет значение true, а std::is_convertible_v<const T&, const CharT*> имеет значение false.

Какой смысл требования 1 при наличии требования 3 ? Что произойдет, если требование 1 будет удалено (например, какой код не будет компилироваться / работать должным образом и т. Д. c)?

PS Происхождение требований: https://cplusplus.github.io/LWG/lwg-defects.html#2758.

РЕДАКТИРОВАТЬ. Разъяснение того, что именно я не понимаю.

Так же, как сейчас с требованиями 1-3, реализация append в MSV C делает точно 1-3:

    template <class _StringViewIsh, class = _Is_string_view_ish<_StringViewIsh>>
    basic_string& append(const _StringViewIsh& _Right) {
        const basic_string_view<_Elem, _Traits> _As_view = _Right;
        return append(_As_view.data(), _Convert_size<size_type>(_As_view.size()));
    }

Обратите внимание, как выполняется требование 1 - буквально:

        const basic_string_view<_Elem, _Traits> _As_view = _Right;

Так почему требования в стандарте не были сформулированы как-то вроде:

  1. Добавляет все символы из t как будто на append(t.data(), t.size()).
  2. Эта перегрузка участвует только в разрешении перегрузки, если std::is_convertible_v<const T&, std::basic_string_view<CharT, Traits>> истинно и std::is_convertible_v<const T&, const CharT*> ложно.

, и тогда реализация станет такой:

    template <class _StringViewIsh, class = _Is_string_view_ish<_StringViewIsh>>
    basic_string& append(const _StringViewIsh& _Right) {
        return append(_Right.data(), _Convert_size<size_type>(_Right.size()));
    }

и больше не будет создавать абсолютно ненужную локальную переменную с копией ее аргумента.

Ответы [ 2 ]

1 голос
/ 23 февраля 2020

Вы неправильно читаете cppreference (и стандарт). Ни в одном документе не прописаны «требования», как вы. Они оба проводят различие между требованиями (когда функция участвует в разрешении перегрузки) и поведением (что делает функция, когда она выполняется). Ваш список объединяет два.

Например, ваш элемент 1 является частью поведения функции; он говорит, что функция должна конвертировать t в string_view. Если вы уберете пункт 1, то функция не сделает этого, и поэтому она станет неработоспособной.

Вопрос, который вы, вероятно, задаете, заключается в том, почему бы просто не иметь перегрузку, которая принимает string_view как параметр вместо шаблона с кучей лишних вещей в нем?

Причина этого изложена в том, что вы указали. Поскольку assign и аналогичные функции уже имеют перегрузку, которая принимает const char*, прямая передача строкового литерала создаст неоднозначность между перегрузкой const char* и перегрузкой string_view, поскольку обе они неявно преобразуются из строкового литерала.

Бит «эта перегрузка участвует только в разрешении перегрузки, если» - это то, что предотвращает возникновение этой неоднозначности. Вот почему в нем прямо говорится, что T не может быть преобразовано в const CharT*. Но принятие такого запрета требует, чтобы тип параметра, который принимает функция, не был string_view; вместо этого он должен быть выведен в точке вызова из типа параметра. Таким образом, это должен быть некоторый тип параметра шаблона T, чтобы можно было реализовать требование разрешения перегрузки.

И это означает, что реализация функции должна выполнить преобразование в string_view.


Добавляет все символы из t как к добавлению (t.data (), t.size ()).

t не может string_view; требуется только, чтобы t был конвертируемым в string_view. Таким образом, t.data() может не быть функцией, которую реализует t.

Если вам нужен пример такого типа, это будет любой тип строки (кроме * 1046). *) который конвертируется в string_view (и не конвертируется в const CharT*). А поскольку string_view является таким полезным типом строк в языке lingua-franca, многие пользовательские типы строк были преобразованы в string_view.

.
0 голосов
/ 23 февраля 2020

Часть 3 просто говорит, что:

  • функция даже не будет вызвана, если преобразование в string_view обречено на неудачу (например, static_assert будет действительно раздражающим; вы хотите, чтобы компилятор попробовал одну из других append перегрузок), а
  • есть исключение, при котором преобразование в const CharT* не считается. В противном случае была бы двусмысленность с basic_string& append( const CharT* s );

По сути, он описывает классический c вариант использования для enable_if.

Это не противоречит части 1 и является ли он избыточным с частью 1, потому что часть 1 просто говорит, что произойдет, когда будет вызвана функция . Если бы часть 1 была удалена, ничего бы не произошло, потому что часть 1 описывает, что делает функция.

...