Почему я не могу создать std::string_view из итераторов std::строк?

#c #c 17 #stdstring #string-view

Вопрос:

Можно легко создать а std::string_view из std::string а. Но если я хочу создать строковое представление диапазона std::string , использование итераторов std::string не работает.

Вот код, который я попробовал: https://gcc.godbolt.org/z/xrodd8PMq

 #include <iostream>
#include <string>
#include <string_view>
#include <iterator>

int main()
{
    std::string str{"My String"};
    std::string_view strView{str};  // works
    //std::string_view strSubView{str.begin(), str.begin()   2}; // error
}
 

Конечно , возможно, мы можем сделать подстроку из str и использовать для создания строкового представления strSubView , но есть дополнительное создание строки.

Я обнаружил, что std::basic_string_view пятый конструктор s принимает диапазон итераторов.

 template<class It, class End>
constexpr basic_string_view(It first, End last);
 

Но являются ли это только итераторами std::string или просто std::basic_string_view самим собой? Если бы не std::string итерации, почему бы нам не иметь его, в конце концов, строковое представление:

описывает объект, который может ссылаться на постоянную непрерывную последовательность объектов, похожих на символы !

Принимая диапазон непрерывной последовательности символов, разве мы не должны считать?

Комментарии:

1. Этот конструктор добавлен в C 20. Если вы компилируете с помощью компилятора C 17, то его нет. Это работает, если вы ориентируетесь на C 20

2.также можно создавать подстроки std::string_view «мы можем сделать подстроку из str… но есть создание дополнительной строки».. strSubView = strView.substr( 0, 2 ); . Это было бы сопоставимо с тем, что вы пытаетесь сделать.

3. @Caleth О да, это было прямо у меня перед глазами, но я не заметил языковой тег в cppreference. Это означает, что в C 17 нет способа сделать это??

Ответ №1:

Этот конструктор добавлен в C 20. Если вы компилируете с помощью компилятора C 17, то его нет.

Вы можете написать функцию, которая делает то же самое

 std::string_view range_to_view(std::string::iterator first, std::string::iterator last) {
    return first != last ? { first.operator->(), last - first } : { nullptr, 0 };
}
 

Комментарии:

1. Я надеюсь, что std::string_view будет встроен в то место, где я когда-либо вызывал range_to_view ?

2. @Const это нематериализованный временный объект, он создается на месте на месте вызова

3. В случае, если first точки заканчиваются, operator-> произойдет сбой

4. @DmytroOvdiienko нет, вы получите указатель на один конец

5. В теле operator->() STL MSVC есть проверка, которая приведет к сбою приложения в случае, если вы попытаетесь вызвать эту функцию по end указателю. И g не удается его скомпилировать godbolt.org/z/fGxfnoxhd

Ответ №2:

Почему я не могу создать std::string_view из итераторов std::string?

Вы можете, но только начиная с C 20.

Но являются ли это только итераторами строки std::или просто самой строкой std::basic_string_view?

Это любые непрерывные итераторы соответствующего типа значений. Принимаются как итераторы строк, так и итераторы строкового представления. Точные ограничения перечислены в документации.

Это означает, что в C 17 нет способа сделать это??

Невозможно использовать несуществующий конструктор, но есть несколько способов получить желаемое строковое представление. Вот один из них:

 std::string_view strSubView = strView.substr(0, 2);
 

или без промежуточной переменной:

 std::string_view strSubView = std::string_view{str}.substr(0, 2);
 

или если у вас есть только эти итераторы и у вас нет возможности получить доступ к строке:

 std::string_view strSubView {amp;*first, last - first};
 

Комментарии:

1. Спасибо за объяснение, я предпочитаю вспомогательную функцию, пока не смогу использовать C 20.

2. amp;*first может произойти сбой, если first указывает на end

3. amp;*first это то же first.operator->() самое, что и в ответе Калета, верно?

4. @Peter-Восстановите монику Эффективно, да.

Ответ №3:

Поскольку разыменование итератора небезопасно (т. Е. Вызов operator* итератора и operator-> на итераторе, который указывает на конец, может привести к сбою вашего приложения), единственный способ в C 17, который я вижу, — это вызывать string_view::substr() и использовать итераторы только для вычисления размера и индекса.

 constexpr std::string_view make_string_view(std::string_view str, 
                                            std::string_view::iterator first, 
                                            std::string_view::iterator last) noexcept
{
    return str.substr(first - str.begin(), last - first);
}
 

И для std:string использования std::string::data() в качестве начального указателя.

 std::string_view make_string_view(std::string constamp; str, 
                                  std::string::const_iterator first, 
                                  std::string::const_iterator last) noexcept
{
    return std::string_view(str.data()   (first - str.begin()), last - first);
}