通过 std::string_view 在 std::string 内擦除

Erase inside a std::string by std::string_view

我需要找到并删除字符串的一部分(子字符串)。 string_view 似乎是个好主意,但我不能让它与 string::erase 一起使用:

// guaranteed to return a view into `str`
auto gimme_gimme_gimme(const std::string& str) -> std::string_view;

auto after_midnight(std::string& str)
{
    auto man = gimme_gimme_gimme(str);

    str.erase(man); // way to hopeful, not a chance though
    str.erase(man.begin(), man.end()); // nope
    str.erase(std::distance(str.begin(), man.begin()), man.size()); // nope
    str.erase(std::distance(str.data(), man.data()), man.size()); // nope again

    // for real???
}

我是不是想多了?给定一个 std::string_view 到一个 std::string 如何擦除字符串的那部分?还是我误用了 string_view

Am I overthinking this?

除非我遗漏了一些明显的东西,否则你正在考虑它。要编译代码,您需要:

auto gimme_gimme_gimme(const std::string& str) -> std::string_view;

auto after_midnight(std::string& str)
{
    auto man = gimme_gimme_gimme(str);

    str.erase(std::distance(std::as_const(str).data(), man.data()), man.size()); // urrr... growling in pain
}

但是等等!!还有更多!注意我说的是 "to make it compile"。代码容易出错!!因为...

std::string::data 不能是 nullptr 但空 string_view 可以表示为(字符串内的有效指针 + 大小 0)或(nullptr + 大小 0).如果 string_view::datanulltpr,就会出现问题,因为使用了 std::distance

所以你需要确保 string_view 总是指向字符串内部,即使视图是空的。或者在擦除端做额外的检查。

字符串视图确实可以是空的,也可以是容器外部的视图。您建议的 erase 重载以及您答案中函数的实现依赖于一个前提条件,即字符串视图是同一个字符串对象。

当然,迭代器重载非常相似并且依赖于相同的前置条件。但是这样的前置条件对于迭代器来说是常规的,但是对于字符串视图来说是非常规的。

在这种情况下,我认为字符串视图不是表示子范围的理想方式。相反,我建议使用基于指数的相对子范围。例如:

struct sub_range {
    size_t begin;
    size_t count;
    constexpr size_t past_end() noexcept {
        return begin + count;
    }
};

对于第二个成员是使用end(即past_end)还是count,并将另一个作为函数提供,这是一个品味问题。无论如何,应该不会混淆,因为该成员将有一个名字。对于索引,使用计数更为常规。

另一种选择是使用有符号索引还是无符号索引。带符号的索引可用于表示向后的范围。 std::string 但是界面不理解这样的范围。

用法示例:

auto gimme_gimme_gimme(const std::string& str) -> sub_range;

auto after_midnight(std::string& str)
{
    auto man = gimme_gimme_gimme(str);
    str.erase(man.begin, man.distance);
}