如何将std::string_view转换为const char*?

75

使用带有标志-std=c++17的gcc-7.1进行编译时,以下程序会引发错误:

#include <string_view>
void foo(const char* cstr) {}
void bar(std::string_view str){
    foo(str);
}

错误信息为

In function 'void bar(std::string_view)':
error: cannot convert 'std::string_view {aka std::basic_string_view<char>}' to 'const char*' for argument '1' to 'void foo(const char*)'
 foo(str);

我很惊讶没有将其转换为 const char*,因为其他库(如abseil、bde)提供了类似的 string_view 类,可以隐式转换为 const char*

2
我的错误,std::std::basic_string_view::data 不保证一个以 null 结尾的字符串。 - François Andrieux
4个回答

99

std::string_view没有提供转换为const char*的功能,因为它没有存储以null结尾的字符串。它存储了指向第一个元素的指针和字符串的长度。这意味着您不能将其传递给需要以null结尾的字符串(如期望const char*的函数foo)(否则,您怎么获得大小?),因此决定不值得。

如果您确定在视图中有以null结尾的字符串,则可以使用std::string_view::data

如果不确定是否应该首先使用std::string_view,请重新考虑,因为如果您想要保证以null结尾的字符串,则需要使用std::string。对于一行代码,您可以使用std::string(object).data()注意:返回值指向将在表达式结束后被销毁的临时std::string实例!)。


std::string_view为什么不提供转换的解释很好,但只是对问题提供了一个临时答案。 - Syndog
1
你关于 std::string(object).data() 的建议是错误的,因为在大多数情况下,数据将指向未分配的存储空间。 - rems4e
13
当我看到建议使用std::string(object).data()时,我的内心也产生了警觉。重要的是要注意,返回的指针指向临时std::string对象中的数据,该对象仅在最大封闭表达式结束之前存在。请注意这一点。 - Don Hatch
2
请从答案中删除 std::string(object).data() 或提供一个易于识别的警告。这里的 std::string(object) 会被释放,因此生成的 C 字符串是未定义的。我看到这个片段在互联网上和各种代码库中被粘贴了很多次。这个答案已经造成了巨大的损害。 - 23scurtu
4
在Stack Overflow上,我们非常关注那些盲目遵循“模仿船长”的白痴,他们把从Stack Overflow复制粘贴作为生存方式,而不是运用自己的大脑。我不明白这有什么值得考虑的。你要么需要将一个临时字符串传递给某个函数,它会对它进行一些处理,要么就不需要。鉴于问题,这是完全可以接受的。为什么有人要关心一些白痴无法阅读并盲目复制不理解的东西呢?这样的白痴根本不应该拥有工作,更不能被Stack Overflow答案保护。 - user11877195

60

只需执行 std::string(string_view_object).c_str() 即可获得一个有保证的空结尾临时副本(并在行末清理它)。

这是必要的,因为字符串视图不保证空结尾。例如,您可能会查看较长缓冲区中间的内容。

如果此用例很昂贵,并且已经证明是瓶颈,可以编写增强的 string_view 来跟踪其是否为空结尾(基本上,如果它是从原始 char const* 构造的)。

然后,您可以编写一个帮助类型,将此增强的 string_view 复制到 std::string 中或直接存储增强的 string_view,并具有将其隐式转换为返回正确空结尾缓冲区的 char const* 的功能。

然后,在代码库的每个地方都使用该增强型帮助程序类型代替 string_view,还可以通过与 std string 的字符串视图交互来增强,以捕获视图到达 std string 缓冲区末尾的情况。

但实际上,这可能有些过度设计了。

更好的方法可能是重新编写接受 const char* 的 API,以接受 string_view


2
通过 "(并在行末清理它)。",我可以继续使用那个 Rvalue 的成员直到 std::string(string_view_object).c_str() 所在的作用域结束,对吗? - sandthorn
3
@sandthorn 不是在作用域结束,而是在完整表达式结束之前。 - Yakk - Adam Nevraumont
2
如果您想让字符串保留下来,您可能更喜欢使用std::string TempVar(string_view_object);,然后在需要它的范围内使用TempVar.c_str() - Ruzihm

11
你可以调用foo(std::string(str).c_str())

0
使用 object.data() 解决了我的编译器错误 C2664 问题。

1
你确定你没有引入一个 bug 吗?请查看其他答案。 - Bob__
对于我的情况,我需要一个指向 string_view 开始的指针,我使用了 begin() 而不是 data(),导致出现错误 C2664,然后我将其更改为 data()。 但我不确定是否引入了 bug,请您提供更多细节信息。 - werber bang
1
这取决于你的用例。 OP 的用例是将整个string_view传递给仅接受const char*的函数。这些函数通常期望以null结尾char数组,而这并不像std::string::data()(自C++11以来)那样由std::string_view::data()保证。 - Bob__
3
你不应该那样做!std::string_view不能保证它所指向的字符串以空字符结尾。你应该使用static_cast<std::string>(object).c_str(),虽然这会产生一个临时副本,但是它是安全的并且具有空字符结尾。 - IGR94

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