std::basic_string<CharT,Traits,Allocator>::resize_and_overwrite

出自cppreference.com
< cpp‎ | string‎ | basic string
 
 
 
std::basic_string
 
template< class Operation >
constexpr void resize_and_overwrite( size_type count, Operation op );
(C++23 起)

重設字符串大小以含有至多 count 個字符,用用戶提供的操作 op 修改可能不確定的內容並設置長度。這樣就避免了初始化大小合適的 std::string 時所產生的成本,當它打算作為要被填充的字符數組時,例如,C API 調用。

此函數執行下列步驟:

  1. 獲得至少含有 count + 1 個字符的存儲,並使它的前 k 個字符等於 *this 的前 k 個字符,其中 kcount 與調用 resize_and_overwritesize() 的結果的較小者。令 p 代表指向該存儲中首個字符的指針。
    • 相等性以如同檢查 this->compare(0, k, p, k) == 0 確定。
    • [p + kp + count] 中的字符可能擁有不確定值。
  2. 求值 std::move(op)(p, count),令它的返回值為 r
  3. [pp + r) 替換 *this 的內容(這會設置 *this 的長度為 r)。使所有指向範圍 [pp + count] 中的指針與引用失效。

如果 r 不具有整數式類型,那麼程序非良構。

如果滿足以下任意條件,那麼行為未定義:

  • std::move(op)(p, count) 拋出異常。
  • std::move(op)(p, count) 修改 pcount
  • r 不在範圍 [0count] 中。
  • 範圍 [pp + r) 中的任何字符擁有不確定值。

推薦實現避免不必要的複製與分配,例如通過使得 p 等於指向調用後為 *this 分配的字符存儲的起始指針,如果 count 小於或等於 capacity(),那麼該存儲能等同於 *this 的既存存儲。

目錄

[編輯] 參數

count - 字符串的最大可能的新大小
op - 用於設置字符串新內容的函數對象

[編輯] 異常

如果 count > max_size(),那麼就會拋出 std::length_error。 對應的 Allocator 所拋的任何異常。

如果從 std::move(op)(p, count) 拋出異常,那麼行為未定義。否則拋出異常的情況下此函數無效果。

[編輯] 註解

無論重分配是否出現,resize_and_overwrite 都使所有指向 *this 中的迭代器、指針及引用失效。實現可以假設調用 resize_and_overwrite 後字符串的內容不會被別名化。

功能特性測試 標準 功能特性
__cpp_lib_string_resize_and_overwrite 202110L (C++23) std::basic_string::resize_and_overwrite

[編輯] 示例

測試這個示例的鏈接:compiler explorer

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <string>
#include <string_view>
static_assert(__cpp_lib_string_resize_and_overwrite);
 
constexpr std::string_view fruits[]{"apple", "banana", "coconut", "date", "elderberry"};
 
int main()
{
    // 简单情况下,仅后附 fruits[0]。字符串大小会增长。
    std::string s{"Food: "};
    s.resize_and_overwrite(16, [sz = s.size()](char* buf, std::size_t buf_size) noexcept
    {
        const auto to_copy = std::min(buf_size - sz, fruits[0].size());
        std::memcpy(buf + sz, fruits[0].data(), to_copy);
        return sz + to_copy;
    });
    std::cout << "1. " << std::quoted(s) << '\n';
 
    // 缩减大小的情况。注意,总是调用用户的 lambda 表达式。
    s.resize_and_overwrite(10, [](char* buf, int n) noexcept
    {
        return std::find(buf, buf + n, ':') - buf;
    });
    std::cout << "2. " << std::quoted(s) << '\n';
 
    std::cout << "3. 复制数据直至缓冲区满。打印数据和大小。\n";
    std::string food{"Food:"};
    const auto resize_to{27};
    std::cout << "起初,food.size: " << food.size()
              << ", food.capacity: " << food.capacity()
              << ", resize_to: " << resize_to
              << ", food: " << std::quoted(food) << '\n';
 
    food.resize_and_overwrite
    (
        resize_to,
        [food_size = food.size()](char* p, std::size_t n) noexcept -> std::size_t
        {
            // p[0]..p[n] 为可赋值范围
            // p[0]..p[min(n, food_size) - 1] 为可读取范围
            // (其内容起初等于原字符串)
 
            // 调试打印:
            std::cout << "Operation() 内; n: " << n << '\n';
 
            // 在空间足够时,把水果复制到缓冲区 p 中。
            char* first = p + food_size;
 
            for (char* const end = p + n; const std::string_view fruit : fruits)
            {
                char* last = first + fruit.size() + 1;
                if (last > end)
                    break;
                *first++ = ' ';
                std::ranges::copy(fruit, first);
                first = last;
            }
 
            const auto final_size{static_cast<std::size_t>(first - p)};
 
            // 调试打印:
            std::cout << "Operation() 内; final_size: " << final_size << '\n';
 
            assert(final_size <= n);
            return final_size; // 返回值为字符串的实际新长度,必定在范围 0..n 中
        }
    );
 
    std::cout << "最后, food.size: " << food.size()
              << ", food.capacity: " << food.capacity()
              << ", food: " << std::quoted(food) << '\n';
}

可能的輸出:

1. "Food: apple"
2. "Food"
3. 复制数据直至缓冲区满。打印数据和大小。
起初, food.size: 5, food.capacity: 15, resize_to: 27, food: "Food:"
Operation() 中; n: 27
Operation() 中; final_size: 26
最后, food.size: 26, food.capacity: 30, food: "Food: apple banana coconut"

[編輯] 參閱

更改存儲的字符數
(公開成員函數) [編輯]