最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++17中std::string_view的使用

 更新時(shí)間:2024年01月22日 15:33:52   作者:流星雨愛(ài)編程  
std::string_view是C++17標(biāo)準(zhǔn)庫(kù)中的一種新類(lèi)型,它提供了對(duì)一個(gè)字符序列的非擁有式視圖,本文主要介紹了C++17中std::string_view的使用,具有一定的參考價(jià)值,感興趣的可以了解一下

1.引言

在C/C++日常編程中,我們常進(jìn)行數(shù)據(jù)的傳遞操作,比如,將數(shù)據(jù)傳給函數(shù)。當(dāng)數(shù)據(jù)占用的內(nèi)存較大時(shí),減少數(shù)據(jù)的拷貝可以有效提高程序的性能。在C++17之前,為了接收只讀字符串的函數(shù)選擇形參一直是一件進(jìn)退兩難的事情,它應(yīng)該是const char*嗎?那樣的話(huà),如果客戶(hù)用std::string,這必須使用c_str()或data()來(lái)獲取const char*。更糟糕的是,函數(shù)講失去std::string良好的面向?qū)ο蟮姆椒捌淞己玫妮o助方法?;蛟S,形參應(yīng)改用const std::string&?這種情況下,始終需要std::string。例如,如果傳遞一個(gè)字符串字面量,編譯器將默認(rèn)創(chuàng)建一個(gè)臨時(shí)字符串對(duì)象(其中包括字符串字面量的副本),并將該對(duì)象傳遞給函數(shù),因此會(huì)增加一點(diǎn)開(kāi)銷(xiāo)。有時(shí),人們會(huì)編寫(xiě)同一函數(shù)的多個(gè)重載版本,一個(gè)接收const char*,另一個(gè)接收const std::string&,單顯然,這并不是一個(gè)優(yōu)雅的解決方案,而且std::string的substr函數(shù),每次都要返回一個(gè)新生成的子串,很容易引起性能問(wèn)題。實(shí)際上我們本意不是要改變?cè)址?,為什么不在原字符串基礎(chǔ)上返回呢?

在C++17中引入了std::string_view,就很好的解決了上面這些問(wèn)題。

2.原理分析

std::string_view是字符串的視圖版本,它能讓我們像處理字符串一樣處理字符序列,而不需要為它們分配內(nèi)存空間。也就是說(shuō),std::string_view類(lèi)型的對(duì)象只是引用一個(gè)外部的字符序列,而不需要持有它們。因此,一個(gè)字符串視圖對(duì)象可以被看作字符串序列的引用 。

使用字符串視圖的開(kāi)銷(xiāo)很小,速度卻很快(以值傳遞一個(gè)std::string_view的開(kāi)銷(xiāo)總是很?。?。然而,它也有一些潛在的危險(xiǎn),就和原生指針一樣,在使用string_view時(shí)也必須由程序員自己來(lái)保證引用的字符串序列是有效的。那么,std::string_view為什么能做到開(kāi)銷(xiāo)小,速度很快呢?我們接著往下看,本文以VS2019平臺(tái)展開(kāi)講解std::string_view的原理和深層次用法。

2.1.結(jié)構(gòu)

std::string_view的類(lèi)UML圖如下:

765798436ef5465da246f9e8439de0fa.png

std::string_view是std::basic_string_view<char>的特化版本,std::basic_string_view的所有接口都適用于std::std::string_view。對(duì)于使用寬字符集,例如Unicode或者某些亞洲字符集的字符串,還定義了其它幾個(gè)版本:

#ifdef __cpp_lib_char8_t
using u8string_view = basic_string_view<char8_t>;
#endif // __cpp_lib_char8_t
using u16string_view = basic_string_view<char16_t>;
using u32string_view = basic_string_view<char32_t>;
using wstring_view   = basic_string_view<wchar_t>;

std::basic_string_view的內(nèi)部結(jié)構(gòu):

template <class _Elem, class _Traits>
class basic_string_view { // wrapper for any kind of contiguous character buffer
public:    
    using traits_type            = _Traits;
    using value_type             = _Elem;
    using pointer                = _Elem*;
    using const_pointer          = const _Elem*;
    using reference              = _Elem&;
    using const_reference        = const _Elem&;
    using const_iterator         = _String_view_iterator<_Traits>;
    using iterator               = const_iterator;
    using const_reverse_iterator = _STD reverse_iterator<const_iterator>;
    using reverse_iterator       = const_reverse_iterator;
    using size_type              = size_t;
    using difference_type        = ptrdiff_t;
    //...
private:
    const_pointer _Mydata;
    size_type _Mysize;
};

從中可以看出std::basic_string_view只存儲(chǔ)了{(lán)_Mydata,_Mysize}兩個(gè)元素,不會(huì)具體存儲(chǔ)原數(shù)據(jù),僅僅存儲(chǔ)指向的數(shù)據(jù)的起始指針和長(zhǎng)度,所以這個(gè)開(kāi)銷(xiāo)是非常小的。

2.2.構(gòu)造函數(shù)

std::basic_string_view的構(gòu)造函數(shù)如下:

    //構(gòu)造函數(shù)
    constexpr basic_string_view() noexcept : _Mydata(), _Mysize(0) {}  //1

    constexpr basic_string_view(const basic_string_view&) noexcept = default; //2
    constexpr basic_string_view& operator=(const basic_string_view&) noexcept = default; //3

    /* implicit */ constexpr basic_string_view(_In_z_ const const_pointer _Ntcts) noexcept // strengthened
        : _Mydata(_Ntcts), _Mysize(_Traits::length(_Ntcts)) {}  //4

    constexpr basic_string_view(
        _In_reads_(_Count) const const_pointer _Cts, const size_type _Count) noexcept // strengthened
        : _Mydata(_Cts), _Mysize(_Count) {
#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_VERIFY(_Count == 0 || _Cts, "non-zero size null string_view");
#endif // _CONTAINER_DEBUG_LEVEL > 0
    }                                                         //5

#ifdef __cpp_lib_concepts
    // clang-format off
    template <contiguous_iterator _It, sized_sentinel_for<_It> _Se>
        requires (is_same_v<iter_value_t<_It>, _Elem> && !is_convertible_v<_Se, size_type>)
    constexpr basic_string_view(_It _First, _Se _Last) noexcept(noexcept(_Last - _First)) // strengthened
        : _Mydata(_STD to_address(_First)), _Mysize(static_cast<size_type>(_Last - _First)) {}  //6
    // clang-format on
#endif // __cpp_lib_concepts

    // 從迭代器中獲取地址函數(shù)to_address

從上面的源碼可以看出,td::basic_string_view的構(gòu)造函數(shù)有:

1)std::string_view a;  調(diào)用第1個(gè)構(gòu)造函數(shù)

2)std::string_view  a("123"); 調(diào)用第4個(gè)構(gòu)造函數(shù),改函數(shù)里面調(diào)用了_Traits::length函數(shù)

_NODISCARD static _CONSTEXPR17 size_t length(_In_z_ const _Elem* const _First) noexcept /* strengthened */ {
        // find length of null-terminated string
#if _HAS_CXX17
#ifdef __cpp_char8_t
        if constexpr (is_same_v<_Elem, char8_t>) {
#if _HAS_U8_INTRINSICS
            return __builtin_u8strlen(_First);
#else // ^^^ use u8 intrinsics / no u8 intrinsics vvv
            return _Primary_char_traits::length(_First);
#endif // _HAS_U8_INTRINSICS
        } else
#endif // __cpp_char8_t
        {
            return __builtin_strlen(_First);
        }
#else // _HAS_CXX17
        return _CSTD strlen(reinterpret_cast<const char*>(_First));
#endif // _HAS_CXX17
    }

_Traits::length函數(shù)又調(diào)用了strlen計(jì)算了字符串的長(zhǎng)度;構(gòu)造了一個(gè)3長(zhǎng)度的std::string_view。

3)std::string_view  a("122323423",  5); 調(diào)用第5個(gè)構(gòu)造函數(shù),把內(nèi)容和長(zhǎng)度構(gòu)造函數(shù),構(gòu)建了一個(gè)內(nèi)容為"12232",長(zhǎng)度為5的std::string_view。

4)構(gòu)造函數(shù)還可以接收迭代器,如下:

char b[] = "121212e124124";
std::string_view  e(std::begin(b), std::end(b));

調(diào)用第6個(gè)構(gòu)造函數(shù),里面調(diào)用to_address通過(guò)迭代器獲取指針:

template <class _Ty, class = void>
inline constexpr bool _Has_to_address_v = false; // determines whether _Ptr has pointer_traits<_Ptr>::to_address(p)

template <class _Ty>
inline constexpr bool
    _Has_to_address_v<_Ty, void_t<decltype(pointer_traits<_Ty>::to_address(_STD declval<const _Ty&>()))>> = true;

template <class _Ty>
_NODISCARD constexpr _Ty* to_address(_Ty* const _Val) noexcept {
    static_assert(!is_function_v<_Ty>,
        "N4810 20.10.4 [pointer.conversion]/2: The program is ill-formed if T is a function type.");
    return _Val;
}

template <class _Ptr>
_NODISCARD constexpr auto to_address(const _Ptr& _Val) noexcept {
    if constexpr (_Has_to_address_v<_Ptr>) {
        return pointer_traits<_Ptr>::to_address(_Val);
    } else {
        return _STD to_address(_Val.operator->()); // plain pointer overload must come first
    }
}

5)拷貝構(gòu)造函數(shù)和賦值構(gòu)造函數(shù)都是使用的系統(tǒng)默認(rèn)函數(shù),類(lèi)似memcpy,直接把_Mydata,_Mysize的值拷貝到另外對(duì)象,比較簡(jiǎn)單,就不贅述了。

上面的都好理解,唯一需要說(shuō)明的是:為什么我們代碼string_view test(string("123"))可以編譯通過(guò),但為什么沒(méi)有對(duì)應(yīng)的構(gòu)造函數(shù)?

實(shí)際上這是因?yàn)閟td::string類(lèi)重載了std::string到std::string_view的轉(zhuǎn)換操作符:

operator std::basic_string_view<CharT, Traits>() const noexcept;

所以,std::string_view test(std::string("123"))實(shí)際執(zhí)行了兩步操作:

a.std::string("abc")轉(zhuǎn)換為std::string_view對(duì)象
b.test調(diào)用了第2個(gè)構(gòu)造函數(shù)生成std::string_view對(duì)象

2.3.成員函數(shù)

1)獲取容量的函數(shù)

    _NODISCARD constexpr size_type size() const noexcept {
        return _Mysize;
    }

    _NODISCARD constexpr size_type length() const noexcept {
        return _Mysize;
    }

    _NODISCARD constexpr bool empty() const noexcept {
        return _Mysize == 0;
    }

    _NODISCARD constexpr size_type max_size() const noexcept {
        // bound to PTRDIFF_MAX to make end() - begin() well defined (also makes room for npos)
        // bound to static_cast<size_t>(-1) / sizeof(_Elem) by address space limits
        return (_STD min)(static_cast<size_t>(PTRDIFF_MAX), static_cast<size_t>(-1) / sizeof(_Elem));
    }

2) 迭代器相關(guān)的函數(shù)

_NODISCARD constexpr const_iterator begin() const noexcept {
#if _ITERATOR_DEBUG_LEVEL >= 1
        return const_iterator(_Mydata, _Mysize, 0);
#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv
        return const_iterator(_Mydata);
#endif // _ITERATOR_DEBUG_LEVEL
    }

    _NODISCARD constexpr const_iterator end() const noexcept {
#if _ITERATOR_DEBUG_LEVEL >= 1
        return const_iterator(_Mydata, _Mysize, _Mysize);
#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv
        return const_iterator(_Mydata + _Mysize);
#endif // _ITERATOR_DEBUG_LEVEL
    }

    _NODISCARD constexpr const_iterator cbegin() const noexcept {
        return begin();
    }

    _NODISCARD constexpr const_iterator cend() const noexcept {
        return end();
    }

    _NODISCARD constexpr const_reverse_iterator rbegin() const noexcept {
        return const_reverse_iterator{end()};
    }

    _NODISCARD constexpr const_reverse_iterator rend() const noexcept {
        return const_reverse_iterator{begin()};
    }

    _NODISCARD constexpr const_reverse_iterator crbegin() const noexcept {
        return rbegin();
    }

    _NODISCARD constexpr const_reverse_iterator crend() const noexcept {
        return rend();
    }

3)元素訪(fǎng)問(wèn)函數(shù)

    _NODISCARD constexpr const_pointer data() const noexcept {
        return _Mydata;
    }

    _NODISCARD constexpr const_reference operator[](const size_type _Off) const noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_VERIFY(_Off < _Mysize, "string_view subscript out of range");
#endif // _CONTAINER_DEBUG_LEVEL > 0
        return _Mydata[_Off];
    }

    _NODISCARD constexpr const_reference at(const size_type _Off) const {
        // get the character at _Off or throw if that is out of range
        _Check_offset_exclusive(_Off);
        return _Mydata[_Off];
    }
_NODISCARD constexpr const_reference front() const noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_VERIFY(_Mysize != 0, "cannot call front on empty string_view");
#endif // _CONTAINER_DEBUG_LEVEL > 0
        return _Mydata[0];
    }

    _NODISCARD constexpr const_reference back() const noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_VERIFY(_Mysize != 0, "cannot call back on empty string_view");
#endif // _CONTAINER_DEBUG_LEVEL > 0
        return _Mydata[_Mysize - 1];
    }

4)修改器函數(shù)

constexpr void remove_prefix(const size_type _Count) noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_VERIFY(_Mysize >= _Count, "cannot remove prefix longer than total size");
#endif // _CONTAINER_DEBUG_LEVEL > 0
        _Mydata += _Count;
        _Mysize -= _Count;
    }

    constexpr void remove_suffix(const size_type _Count) noexcept /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_VERIFY(_Mysize >= _Count, "cannot remove suffix longer than total size");
#endif // _CONTAINER_DEBUG_LEVEL > 0
        _Mysize -= _Count;
    }

    constexpr void swap(basic_string_view& _Other) noexcept {
        const basic_string_view _Tmp{_Other}; // note: std::swap is not constexpr before C++20
        _Other = *this;
        *this  = _Tmp;
    }

remove_prefix(size_type)和remove_suffix(size_type)方法,前者是將起始指針前移給定的偏移量來(lái)收縮字符串,后者是將結(jié)尾指針倒退給定的偏移量來(lái)收縮字符串,三個(gè)函數(shù)僅會(huì)修改std::string_view的數(shù)據(jù)指向,不會(huì)修改指向的數(shù)據(jù)。

5)查找比較函數(shù)

    _CONSTEXPR20 size_type copy(
        _Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const {
        // copy [_Off, _Off + Count) to [_Ptr, _Ptr + _Count)
        _Check_offset(_Off);
        _Count = _Clamp_suffix_size(_Off, _Count);
        _Traits::copy(_Ptr, _Mydata + _Off, _Count);
        return _Count;
    }

    _Pre_satisfies_(_Dest_size >= _Count) _CONSTEXPR20 size_type
        _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, const size_type _Dest_size, size_type _Count,
            const size_type _Off = 0) const {
        // copy [_Off, _Off + _Count) to [_Dest, _Dest + _Count)
        _Check_offset(_Off);
        _Count = _Clamp_suffix_size(_Off, _Count);
        _Traits::_Copy_s(_Dest, _Dest_size, _Mydata + _Off, _Count);
        return _Count;
    }

    _NODISCARD constexpr basic_string_view substr(const size_type _Off = 0, size_type _Count = npos) const {
        // return a new basic_string_view moved forward by _Off and trimmed to _Count elements
        _Check_offset(_Off);
        _Count = _Clamp_suffix_size(_Off, _Count);
        return basic_string_view(_Mydata + _Off, _Count);
    }

    constexpr bool _Equal(const basic_string_view _Right) const noexcept {
        return _Traits_equal<_Traits>(_Mydata, _Mysize, _Right._Mydata, _Right._Mysize);
    }

    _NODISCARD constexpr int compare(const basic_string_view _Right) const noexcept {
        return _Traits_compare<_Traits>(_Mydata, _Mysize, _Right._Mydata, _Right._Mysize);
    }

    _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, const basic_string_view _Right) const {
        // compare [_Off, _Off + _Nx) with _Right
        return substr(_Off, _Nx).compare(_Right);
    }

    _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, const basic_string_view _Right,
        const size_type _Roff, const size_type _Count) const {
        // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count)
        return substr(_Off, _Nx).compare(_Right.substr(_Roff, _Count));
    }

    _NODISCARD constexpr int compare(_In_z_ const _Elem* const _Ptr) const { // compare [0, _Mysize) with [_Ptr, <null>)
        return compare(basic_string_view(_Ptr));
    }

    _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const {
        // compare [_Off, _Off + _Nx) with [_Ptr, <null>)
        return substr(_Off, _Nx).compare(basic_string_view(_Ptr));
    }

    _NODISCARD constexpr int compare(const size_type _Off, const size_type _Nx,
        _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) const {
        // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count)
        return substr(_Off, _Nx).compare(basic_string_view(_Ptr, _Count));
    }

#if _HAS_CXX20
    _NODISCARD constexpr bool starts_with(const basic_string_view _Right) const noexcept {
        const auto _Rightsize = _Right._Mysize;
        if (_Mysize < _Rightsize) {
            return false;
        }
        return _Traits::compare(_Mydata, _Right._Mydata, _Rightsize) == 0;
    }

    _NODISCARD constexpr bool starts_with(const _Elem _Right) const noexcept {
        return !empty() && _Traits::eq(front(), _Right);
    }

    _NODISCARD constexpr bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ {
        return starts_with(basic_string_view(_Right));
    }

    _NODISCARD constexpr bool ends_with(const basic_string_view _Right) const noexcept {
        const auto _Rightsize = _Right._Mysize;
        if (_Mysize < _Rightsize) {
            return false;
        }
        return _Traits::compare(_Mydata + (_Mysize - _Rightsize), _Right._Mydata, _Rightsize) == 0;
    }

    _NODISCARD constexpr bool ends_with(const _Elem _Right) const noexcept {
        return !empty() && _Traits::eq(back(), _Right);
    }

    _NODISCARD constexpr bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ {
        return ends_with(basic_string_view(_Right));
    }
#endif // _HAS_CXX20

    _NODISCARD constexpr size_type find(const basic_string_view _Right, const size_type _Off = 0) const noexcept {
        // look for _Right beginning at or after _Off
        return _Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize);
    }

    _NODISCARD constexpr size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept {
        // look for _Ch at or after _Off
        return _Traits_find_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch);
    }

    _NODISCARD constexpr size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
        const size_type _Count) const noexcept /* strengthened */ {
        // look for [_Ptr, _Ptr + _Count) beginning at or after _Off
        return _Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count);
    }

    _NODISCARD constexpr size_type find(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept
    /* strengthened */ {
        // look for [_Ptr, <null>) beginning at or after _Off
        return _Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr));
    }

    _NODISCARD constexpr size_type rfind(const basic_string_view _Right, const size_type _Off = npos) const noexcept {
        // look for _Right beginning before _Off
        return _Traits_rfind<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize);
    }

    _NODISCARD constexpr size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept {
        // look for _Ch before _Off
        return _Traits_rfind_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch);
    }

    _NODISCARD constexpr size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
        const size_type _Count) const noexcept /* strengthened */ {
        // look for [_Ptr, _Ptr + _Count) beginning before _Off
        return _Traits_rfind<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count);
    }

    _NODISCARD constexpr size_type rfind(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept
    /* strengthened */ {
        // look for [_Ptr, <null>) beginning before _Off
        return _Traits_rfind<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr));
    }

    _NODISCARD constexpr size_type find_first_of(const basic_string_view _Right,
        const size_type _Off = 0) const noexcept { // look for one of _Right at or after _Off
        return _Traits_find_first_of<_Traits>(
            _Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize, _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept {
        // look for _Ch at or after _Off
        return _Traits_find_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch);
    }

    _NODISCARD constexpr size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
        const size_type _Count) const noexcept /* strengthened */ {
        // look for one of [_Ptr, _Ptr + _Count) at or after _Off
        return _Traits_find_first_of<_Traits>(
            _Mydata, _Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_first_of(
        _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ {
        // look for one of [_Ptr, <null>) at or after _Off
        return _Traits_find_first_of<_Traits>(
            _Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_last_of(const basic_string_view _Right,
        const size_type _Off = npos) const noexcept { // look for one of _Right before _Off
        return _Traits_find_last_of<_Traits>(
            _Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize, _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_last_of(const _Elem _Ch, const size_type _Off = npos) const noexcept {
        // look for _Ch before _Off
        return _Traits_rfind_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch);
    }

    _NODISCARD constexpr size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
        const size_type _Count) const noexcept /* strengthened */ {
        // look for one of [_Ptr, _Ptr + _Count) before _Off
        return _Traits_find_last_of<_Traits>(
            _Mydata, _Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_last_of(
        _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ {
        // look for one of [_Ptr, <null>) before _Off
        return _Traits_find_last_of<_Traits>(
            _Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_first_not_of(const basic_string_view _Right,
        const size_type _Off = 0) const noexcept { // look for none of _Right at or after _Off
        return _Traits_find_first_not_of<_Traits>(
            _Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize, _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_first_not_of(const _Elem _Ch, const size_type _Off = 0) const noexcept {
        // look for any value other than _Ch at or after _Off
        return _Traits_find_not_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch);
    }

    _NODISCARD constexpr size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
        const size_type _Count) const noexcept /* strengthened */ {
        // look for none of [_Ptr, _Ptr + _Count) at or after _Off
        return _Traits_find_first_not_of<_Traits>(
            _Mydata, _Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_first_not_of(
        _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ {
        // look for none of [_Ptr, <null>) at or after _Off
        return _Traits_find_first_not_of<_Traits>(
            _Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_last_not_of(const basic_string_view _Right,
        const size_type _Off = npos) const noexcept { // look for none of _Right before _Off
        return _Traits_find_last_not_of<_Traits>(
            _Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize, _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_last_not_of(const _Elem _Ch, const size_type _Off = npos) const noexcept {
        // look for any value other than _Ch before _Off
        return _Traits_rfind_not_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch);
    }

    _NODISCARD constexpr size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off,
        const size_type _Count) const noexcept /* strengthened */ {
        // look for none of [_Ptr, _Ptr + _Count) before _Off
        return _Traits_find_last_not_of<_Traits>(
            _Mydata, _Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr size_type find_last_not_of(
        _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ {
        // look for none of [_Ptr, <null>) before _Off
        return _Traits_find_last_not_of<_Traits>(
            _Mydata, _Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{});
    }

    _NODISCARD constexpr bool _Starts_with(const basic_string_view _View) const noexcept {
        return _Mysize >= _View._Mysize && _Traits::compare(_Mydata, _View._Mydata, _View._Mysize) == 0;
    }

這些函數(shù)大致std::string的功能一致,在這里我就不贅述了。

2.4.std::string_view字面量

標(biāo)準(zhǔn)的用戶(hù)自定義字面量sv,將字符串字面量解釋為std::string_view,類(lèi)似Qt的QString中的QStringLiteral??丛创a是怎么實(shí)現(xiàn)的:

// basic_string_view LITERALS
inline namespace literals {
    inline namespace string_view_literals {
        _NODISCARD constexpr string_view operator"" sv(const char* _Str, size_t _Len) noexcept {
            return string_view(_Str, _Len);
        }

        _NODISCARD constexpr wstring_view operator"" sv(const wchar_t* _Str, size_t _Len) noexcept {
            return wstring_view(_Str, _Len);
        }

#ifdef __cpp_char8_t
        _NODISCARD constexpr basic_string_view<char8_t> operator"" sv(const char8_t* _Str, size_t _Len) noexcept {
            return basic_string_view<char8_t>(_Str, _Len);
        }
#endif // __cpp_char8_t

        _NODISCARD constexpr u16string_view operator"" sv(const char16_t* _Str, size_t _Len) noexcept {
            return u16string_view(_Str, _Len);
        }

        _NODISCARD constexpr u32string_view operator"" sv(const char32_t* _Str, size_t _Len) noexcept {
            return u32string_view(_Str, _Len);
        }
    } // namespace string_view_literals
} // namespace literals

用戶(hù)就可以這樣定義:

using namespace std::literals::string_view_literals;
using namespace std::string_view_literals;
using namespace std::literals;
using namespace std;
std::string_view sv { "My Hello world"sv };

3.實(shí)例

3.1.std::string_view和std::string的運(yùn)算符操作

當(dāng)我們將std::string_view類(lèi)型的常量弱引用類(lèi)型的字符串和std::string類(lèi)型的字符串進(jìn)行相加(運(yùn)算符+)操作時(shí)會(huì)出錯(cuò),必須要先將string_view轉(zhuǎn)化為const char*,也就是調(diào)用data()接口,測(cè)試代碼如下:

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

int main()
{
	std::string str1 = "hello";
	std::string_view sv1 = " world";
	//使用+號(hào)運(yùn)算符時(shí),必須將string_view轉(zhuǎn)化為const char*
	auto it = str1 + sv1.data();
	//使用append追加字符串不會(huì)出錯(cuò)
	auto it2 = str1.append(sv1);
	std::cout << it2 << std::endl;

	return 0;
}

警告:返回字符串的函數(shù)應(yīng)該返回const std::string&或std::string,但不應(yīng)該返回std::string_view。返回std::string_view會(huì)帶來(lái)使返回的std::string_view無(wú)效的風(fēng)險(xiǎn),例如當(dāng)它指向的字符串需要重新分配時(shí)。

警告:將const std:string&或std::string_view存儲(chǔ)為類(lèi)的數(shù)據(jù)成員需要確保它們指向的字符串在對(duì)象的生命周期內(nèi)保持有效狀態(tài),存儲(chǔ)std::string更安全。

3.2.查找函數(shù)使用

先看一個(gè)例子:

string replace_post(string_view src, string_view new_post)
{
    // 找到點(diǎn)的位置
    auto pos = src.find(".") + 1;
    // 取出點(diǎn)及點(diǎn)之前的全部字符,string_view的substr會(huì)返回一個(gè)
    // string_view對(duì)象,所以要取data()賦值給string對(duì)象
    string s1 = src.substr(0, pos).data();

    // 加上新的后綴
    return s1 + new_post.data();
}
int main()
{
    string_view sv = "abcdefg.xxx";
    string s = replace_post(sv, "yyy");
    cout << sv << " replaced post by yyy result is:" << s << endl;
    return 0;
}

輸出:

abcdefg.xxxyyy

為什么輸出 "abcdefg.xxxyyy" 了呢?那是因?yàn)樵谶@一步string s1 = src.substr(0, pos).data();返回后s1還是 "abcdefg.xxx",std::string_view內(nèi)部只是簡(jiǎn)單地封裝原始字符串的起始位置和結(jié)束位置, 相當(dāng)于給字符串設(shè)置了一個(gè)觀察窗口,用戶(hù)只能看到通過(guò)窗口能看到的那部分?jǐn)?shù)據(jù). data()成員返回的是char*的指針, 是std::string_view內(nèi)部字符串的起始位置. 所以其表現(xiàn)再來(lái)的行為跟C字符串一樣了, 直到遇到空字符串才結(jié)束。

3.3.std::string_view和臨時(shí)字符串

std::string_view并不擁有其指向內(nèi)容的所有權(quán),用Rust的術(shù)語(yǔ)來(lái)說(shuō),它僅僅是暫時(shí)borrow(借用)了它。如果擁有者提前釋放了,你還在使用這些內(nèi)容,那會(huì)出現(xiàn)內(nèi)存問(wèn)題,這跟懸掛指針(dangling pointer)或懸掛引用(dangling references)很像。Rust專(zhuān)門(mén)有套機(jī)制在編譯時(shí)分析變量的生命期,保證borrow的資源在使用期間不會(huì)被釋放,但C++沒(méi)有這樣的檢查,需要人工保證。下面列出一些典型的問(wèn)題情況:

std::string_view sv = std::string{"hello world"}; 
string_view foo() {
    std::string s{"hello world"};
    return string_view{s};
}
auto id(std::string_view sv) { return sv; }

int main() {
    std::string s = "hello";
    auto sv = id(s + " world"); 
}

警告:永遠(yuǎn)不要使用std::string_view保存臨時(shí)字符串的視圖。

4.總結(jié)

std::string_view的優(yōu)點(diǎn):

1)高效性:std:string_view主要用于提供字符串的視圖(view),使std::string_view拷貝字符串的過(guò)程非常高效,永遠(yuǎn)不會(huì)創(chuàng)建字符串的任何副本,不像std::string會(huì)效率低下且導(dǎo)致內(nèi)存開(kāi)銷(xiāo)。std::string_view不擁有字符串?dāng)?shù)據(jù),它僅提供對(duì)現(xiàn)有字符串的視圖或引用(view or reference)。這使得它適合需要訪(fǎng)問(wèn)或處理字符串而無(wú)需內(nèi)存分配或重新分配開(kāi)銷(xiāo)的場(chǎng)景,特別是在處理大量字符串時(shí)非常有用。
2)安全性:由于stdstring_view是只讀的,因此它不能被用來(lái)修改字符串。這使得它成為一個(gè)安全的工具,可以防止由于修改字符串而導(dǎo)致的錯(cuò)誤。
3)  靈活性:stdstring_view可以輕松地與各種字符串類(lèi)型一起使用包括std::string、字符數(shù)組和字符指針。這使得它成為處理字符串的靈活工具。

當(dāng)然任何事物都有它的兩面性,它也有些不足,在一些復(fù)雜的場(chǎng)景的,人工是很難保證指向的內(nèi)容的生命周期足夠長(zhǎng)。所以,推薦的使用方式:僅僅作為函數(shù)參數(shù),因?yàn)槿绻搮?shù)僅僅在函數(shù)體內(nèi)使用而不傳遞出去,這樣使用是安全的。

參考:std::basic_string_view - cppreference.com

到此這篇關(guān)于C++17中std::string_view的使用的文章就介紹到這了,更多相關(guān)C++17 std::string_view內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入了解c++數(shù)組與指針

    深入了解c++數(shù)組與指針

    這篇文章主要介紹了c++數(shù)組與指針的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下
    2020-08-08
  • C++實(shí)現(xiàn)線(xiàn)程同步的四種方式總結(jié)

    C++實(shí)現(xiàn)線(xiàn)程同步的四種方式總結(jié)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)線(xiàn)程同步的四種方式,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C++有一定的幫助,需要的可以參考一下
    2022-11-11
  • C++中成員函數(shù)和友元函數(shù)的使用及區(qū)別詳解

    C++中成員函數(shù)和友元函數(shù)的使用及區(qū)別詳解

    大家好,本篇文章主要講的是C++中成員函數(shù)和友元函數(shù)的使用及區(qū)別詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下
    2022-01-01
  • C++ 11和C++98相比有哪些新特性

    C++ 11和C++98相比有哪些新特性

    C++11標(biāo)準(zhǔn)提供了許多有用的新特性。這篇文章特別針對(duì)使C++11和C++98相比看上去像一門(mén)新語(yǔ)言的特性
    2017-03-03
  • C語(yǔ)言聯(lián)合體類(lèi)型的實(shí)現(xiàn)

    C語(yǔ)言聯(lián)合體類(lèi)型的實(shí)現(xiàn)

    聯(lián)合體也是一種構(gòu)造數(shù)據(jù)類(lèi)型,和結(jié)構(gòu)體類(lèi)型一樣,它也是由各種不同類(lèi)型的數(shù)據(jù)組成,本文主要介紹了C語(yǔ)言聯(lián)合體類(lèi)型的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • c語(yǔ)言?指針零基礎(chǔ)講解

    c語(yǔ)言?指針零基礎(chǔ)講解

    指針是指向另一個(gè)變量的變量。意思是一個(gè)指針保存的是另一個(gè)變量的內(nèi)存地址。換句話(huà)說(shuō),指針保存的并不是普通意義上的數(shù)值,而是另一個(gè)變量的地址值。一個(gè)指針保存了另一個(gè)變量的地址值,就說(shuō)這個(gè)指針“指向”了那個(gè)變量
    2022-02-02
  • C++實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)框架SimpleNN的詳細(xì)過(guò)程

    C++實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)框架SimpleNN的詳細(xì)過(guò)程

    本來(lái)自己想到用C++實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)主要是想強(qiáng)化一下編碼能力并入門(mén)深度學(xué)習(xí),對(duì)C++實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)框架SimpleNN的詳細(xì)過(guò)程感興趣的朋友一起看看吧
    2021-08-08
  • floyd算法實(shí)現(xiàn)思路及實(shí)例代碼

    floyd算法實(shí)現(xiàn)思路及實(shí)例代碼

    這篇文章主要介紹了floyd算法實(shí)現(xiàn)思路及實(shí)例代碼,有需要的朋友可以參考一下
    2014-01-01
  • C++中字符串的查找與插入方式

    C++中字符串的查找與插入方式

    本文介紹了三種查找子字符串的方法,包括暴力匹配、使用函數(shù)和使用循環(huán),同時(shí),介紹了三種插入內(nèi)容的方法,包括直接插入、使用substr截取和使用for循環(huán)逐個(gè)字符拼接,最后強(qiáng)調(diào)了這些方法僅為個(gè)人經(jīng)驗(yàn),希望能提供參考
    2026-04-04
  • 基于atoi()與itoa()函數(shù)的內(nèi)部實(shí)現(xiàn)方法詳解

    基于atoi()與itoa()函數(shù)的內(nèi)部實(shí)現(xiàn)方法詳解

    本篇文章是對(duì)atoi()與itoa()函數(shù)的內(nèi)部實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05

最新評(píng)論

兰考县| 山丹县| 毕节市| 贵港市| 定远县| 乐陵市| 琼中| 威海市| 凤冈县| 百色市| 镇原县| 古丈县| 五家渠市| 靖州| 石狮市| 蒙山县| 新宁县| 屯昌县| 临洮县| 陆丰市| 沁阳市| 黄浦区| 体育| 佛教| 靖远县| 平塘县| 高青县| 阜宁县| 阿鲁科尔沁旗| 磐石市| 施秉县| 天长市| 永兴县| 密山市| 周至县| 莲花县| 巴彦淖尔市| 湖州市| 阿勒泰市| 阳春市| 昔阳县|