通过 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::data
是 nulltpr
,就会出现问题,因为使用了 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);
}
我需要找到并删除字符串的一部分(子字符串)。 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::data
是 nulltpr
,就会出现问题,因为使用了 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);
}