假设我有以下代码:
void some_function(std::string_view view) {
std::cout << view << '\n';
}
int main() {
some_function(std::string{"hello, world"}); // ???
}
view
里面的some_function
会指的是已经被破坏的string
吗?我很困惑,因为考虑到这段代码:
std::string_view view(std::string{"hello, world"});
产生警告(来自
clang++
):
warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl]
有什么区别?
(奇怪的是,使用大括号
{}
而不是方括号()
来初始化上面的string_view
可以消除警告。我也不知道为什么会这样。)
需要明确的是,我理解上面的警告(
string_view
比string
更长寿,所以它持有一个悬空指针)。我要问的是为什么将 string
传递到 some_function
不会产生相同的警告。
some_function(std::string{"hello, world"});
是完全安全的,只要该函数不保留 string_view
供以后使用即可。
临时的
std::string
在这个完整表达式的末尾被销毁(粗略地说,在这个 ;
),所以它在函数返回后被销毁。
std::string_view view(std::string{"hello, world"});
还是
string_view
,
()
始终会产生悬挂的 {}
。如果括号的选择影响编译器警告,则这是编译器缺陷。
std::string_view
就是std::basic_string_view<char>
,所以让我们看看它的cppreference文档:
类模板
描述了一个对象,该对象可以引用类似basic_string_view
的对象的恒定连续序列,序列的第一个元素位于位置 0。char
典型的实现只包含两个成员:指向常量的指针
和大小。CharT
我突出显示的部分告诉我们为什么clang关于
std::string_view view(std::string{"hello, world"});
的说法是正确的:正如其他人评论的那样,这是因为在声明完成后,std::string{"hello, world"}
被销毁,并且std::string_view
所持有的底层指针悬空。
显然这只是一个“典型的实现”,但既然我们知道它是正确的,它至少告诉我们该标准不需要任何实现来执行一些特殊的操作来保持临时对象的存活。
std::string
临时值传递给
参数是否安全? 一般来说,不一定安全。这取决于该函数的作用。如果您不知道,那么您不应该认为它是安全的。std::string_view
了解了所示函数的定义,可以安全地使用临时字符串调用示例函数。
some_function 中的视图是否会引用已被销毁的字符串?
在这种情况下不是,因为字符串视图引用的临时参数字符串尚未被销毁。
函数的参数的生命周期比作为参数传递的临时变量的生命周期短。字符串视图变量的生命周期比传递给构造函数的临时参数的生命周期长。
some_function(std::string{"hello, world"});
是完全安全的,因为它按值传递并保留在作用域内直到函数结束。如果您只关心安全性,那么就可以了,如果性能可能是一个问题,我建议在这里使用右值引用,如下所示:
void some_function(std::string_view&& view)
{
std::cout << "rval reference: " << view << '\n';
}
int main()
{
some_function(std::string{"hello, world"});
}
如果您打算主要将 some_function()
用于临时值,R 值引用 非常有用。