Почему std :: string_view :: data не содержит нулевого терминатора?

Этот код имеет неопределенное поведение:

#include <string_view>
#include <iostream>

using namespace std::string_view_literals;

void foo(std::string_view msg) {
std::cout << msg.data() << '\n'; // undefined behavior if 'msg' is not null-
// terminated

// std::cout << msg << '\n'; is not undefined because operator<< uses
//                           iterators to print 'msg', but that's not the point
}

int main() {
foo("hello"sv); // not null-terminated - undefined behavior
foo("foo");     // same, even more dangerous
}

Причина в том что std::string_view может хранить строки с ненулевым символом в конце и не включает в себя нулевой терминатор при вызове data, Это действительно ограничивает, так как для приведенного выше кода, определяющего поведение, я должен построить std::string из этого:

std::string str{ msg };
std::cout << str.data() << '\n';

Это действительно делает std::string_view в этом случае нет необходимости, мне все еще нужно скопировать строку, переданную fooтак почему бы не использовать семантику перемещения и изменения msg к std::string? Это может быть быстрее, но я не измерял.

В любом случае, необходимо построить std::string каждый раз, когда я хочу передать const char* к функции, которая принимает только const char* это немного излишне, но должна быть причина, почему Комитет решил это так.

Итак, почему std::string_view::data не возвращать строку с нулевым символом в конце, например std::string::data?

6

Решение

Итак, почему std :: string_view :: data не возвращает нулевое окончание
строка типа std :: string :: data

Просто потому, что не может. string_view может быть более узким видом на большую строку (подстроку строки). Это означает, что строка Количество просмотров не обязательно будет иметь нулевое завершение в конце определенного представления. Вы не можете записать нулевой терминатор в основную строку по понятным причинам, и вы не можете создать копию строки и вернуть char * без утечки памяти.

Если вы хотите завершить нулевую строку, вам нужно создать std::string скопировать из него.

Позвольте мне показать хорошее использование std::string_view:

auto tokenize(std::string_view str, Pred is_delim) -> std::vector<std::string_view>

Здесь результирующий вектор содержит токены как представления в большей строке.

16

Другие решения

Цель string_view должен быть диапазоном, представляющим непрерывную последовательность символов. Ограничение такого диапазона диапазоном, заканчивающимся NUL-ограничителем, ограничивает полезность класса.

При этом было бы полезно иметь альтернативную версию string_view который предназначен только для того, чтобы быть созданным из строк, которые действительно заканчиваются NUL.

мой zstring_view класс унаследован от string_viewи предоставляет поддержку для удаления элементов с фронта и других операций, которые не могут сделать строку не завершенной NUL. Это обеспечивает остальные операции, но они возвращают string_viewне zstring_view,

Вы будете удивлены тем, как мало операций вы потеряете от string_view чтобы сделать эту работу:

template<typename charT, typename traits = std::char_traits<charT>>
class basic_zstring_view : private basic_string_view<charT, traits>
{
public:
using base_view_type = basic_string_view<charT, traits>;

using base_view_type::traits_type;
using base_view_type::value_type;
using base_view_type::pointer;
using base_view_type::const_pointer;
using base_view_type::reference;
using base_view_type::const_reference;

using base_view_type::const_iterator;
using base_view_type::iterator;
using base_view_type::const_reverse_iterator;
using base_view_type::reverse_iterator;

using typename base_view_type::size_type;
using base_view_type::difference_type;

using base_view_type::npos;

basic_zstring_view(const charT* str) : base_view_type(str) {}
constexpr explicit basic_zstring_view(const charT* str, size_type len) : base_view_type(str, len) {}
constexpr explicit basic_zstring_view(const base_view_type &view) : base_view_type(view) {}

constexpr basic_zstring_view(const basic_zstring_view&) noexcept = default;
basic_zstring_view& operator=(const basic_zstring_view&) noexcept = default;

using base_view_type::begin;
using base_view_type::end;
using base_view_type::cbegin;
using base_view_type::cend;
using base_view_type::rbegin;
using base_view_type::rend;
using base_view_type::crbegin;
using base_view_type::crend;

using base_view_type::size;
using base_view_type::length;
using base_view_type::max_size;
using base_view_type::empty;

using base_view_type::operator[];
using base_view_type::at;
using base_view_type::front;
using base_view_type::back;
using base_view_type::data;

using base_view_type::remove_prefix;

//`using base_view_type::remove_suffix`; Intentionally not provided.

///Creates a `basic_string_view` that lacks the last few characters.
constexpr basic_string_view<charT, traits> view_suffix(size_type n) const
{
return basic_string_view<charT, traits>(data(), size() - n);
}

using base_view_type::swap;

template<class Allocator = std::allocator<charT> >
std::basic_string<charT, traits, Allocator> to_string(const Allocator& a = Allocator()) const
{
return std::basic_string<charT, traits, Allocator>(begin(), end(), a);
}

constexpr operator base_view_type() const {return base_view_type(data(), size());}

using base_view_type::to_string;

using base_view_type::copy;

using base_view_type::substr;

using base_view_type::operator==;
using base_view_type::operator!=;
using base_view_type::compare;
};
8

По вопросам рекламы [email protected]