使用std :: string_view与需要空终止字符串的API

26

我有一个接受std::string_view参数的方法,它使用一个以空字符结尾的字符串作为参数的函数。例如:

void stringFunc(std::experimental::string_view str) {
    some_c_library_func(/* Expects null terminated string */);
}

问题是,如何正确处理这种情况?str.to_string().c_str() 是唯一的选择吗?我真的想在这个方法中使用 std::string_view,因为我会传递不同类型的字符串。

4个回答

29

我通过创建一个名为zstring_view的备用字符串视图类来解决了这个问题。它是从string_view继承而来,并包含大部分其接口。

主要区别在于,zstring_view不能从string_view创建。另外,任何会移除元素的string_view API都不是界面的一部分,或者它们返回string_view而不是zstring_view

它们可以从任何以NUL结尾的字符串源创建:std::string等等。我甚至为它们创建了特殊的用户定义字面量后缀:_zsv

想法是,只要你不手动将非以NUL结尾的字符串放入zstring_view中,所有的zstring_view都应该是以NUL结尾的。与std::string一样,NUL字符不是字符串的大小的一部分,但它是存在的。

我发现它非常有用,可以处理C接口。


1
我刚刚也是这样做的,我同意界面非常好,消除了以前将const std::string &作为参数的最后一个原因。使用私有继承确实会失去许多便利(例如比较和转换回string_view)-看着接口,似乎唯一有问题的变异器是remove_***fix和swap-我重新实现了swap,并添加了remove_***fix作为私有未实现成员,一切似乎都工作得很好-因此您确实可以公开继承std::string_view。唯一的问题是将来向string_view添加新的变异器。 - Shaggi
1
尽管可以说这是不好的风格。而且,您可能会遇到将zstring_view传递给需要string_view&的函数时出现问题(在两个以上方面都很奇怪...) - Shaggi
1
@Shaggi zstring_view相对安全,可以方便地隐式衰减为string_view。需要注意的是反向顺序。 - VoidStar
同意,但如果您正在公开派生,则在将zstring_view传递给接受string_view&的函数时不会发生转换,这使其不安全。 - Shaggi
这是我尝试实现zstring_view的努力。欢迎任何反馈。https://gist.github.com/tuket/d4c09344a2173b1acc1651d416733fbd - undefined

11

您无法通过 std::string_view 修改字符串。因此,您无法添加终止字符 '\0'。因此,您需要将该字符串复制到其他位置以添加'\0'终止符。如果字符串足够短,可以通过将其放在堆栈中来避免堆分配。如果您知道 std::string_view 是空结尾字符串的一部分,则可以检查结尾后面的字符是否为 '\0' 字符,如果是则避免复制操作。除此之外,我认为没有更多优化的空间。


1
VLA 的使用非常好。 - user1095108
9
@user1095108,但任何允许在C++中使用VLAs的编译器都是应用了非标准扩展,因此我认为VLAs永远不可能是好的。 :P - underscore_d
2
它们通过alloca几乎在任何地方都得到了实际支持,你可以使它们具有可移植性。 - user1095108

5

std::experimental::string_view上,您肯定不应该调用data

与basic_string :: data()和字符串字面值不同,data()可能返回指向未以null结尾的缓冲区的指针。

因此,请在其上调用to_stringc_str

void stringFunc(std::experimental::string_view str) {
    some_c_library_func(str.to_string().c_str());
}

或者:

void stringFunc(std::experimental::string_view str) {
    std::string real_str(str);
    some_c_library_func(real_str.c_str());
}

1
string::data() 返回指向原始字符字符串的指针,该字符串不一定以空字符结尾。如果您需要一个以空字符结尾的字符串,请使用string::c_str()。 - Howard Lee Harkness
4
自从C++11以来,它确实是以空字符结尾的。例如,请参见https://www.cplusplus.com/reference/string/string/data/ - minexew

0
在某些情况下,C风格的函数有重载,可以接受字符串长度作为单独的参数。
例如,可以使用strncasecmp()代替strcasesmp()。当然,在这种特定情况下,如果字符串不相等但前n个字符相等,则需要实现额外的逻辑。
但是,这可能是编写自定义字符串视图类的好选择。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接