【问题标题】:Is it safe to pass an `std::string` temporary into an `std::string_view` parameter?将`std::string`临时传递给`std::string_view`参数是否安全?
【发布时间】:2021-11-07 23:03:10
【问题描述】:

假设我有以下代码:

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_viewstring 寿命长,所以它有一个悬空指针)。我要问的是为什么将string 传递给some_function 不会产生相同的警告。

【问题讨论】:

  • 临时值的生命周期是直到完整表达式结束。在第一个示例的情况下,完整表达式的结尾是 some_function 调用完成时。所以在这种情况下它是有效的。
  • clang 发出警告是正确的。 std::string_view view(std::string{"hello, world"}); 创建一个 string_view ,如果你想避免未定义的行为,你绝不能访问它的数据,因为 view 引用的 char 数组在完整表达式的末尾消失了。另一种用法会起作用,因为临时字符串会一直存在,直到 some_function 返回。
  • 如果将参数类型从std::string_view view 更改为const std::string_view&amp; view,编译器警告会消失吗?
  • @fabian 谢谢,但为什么std::string_view view{std::string{"hello, world"}}(即带大括号)没有产生警告?

标签: c++ c++17 stdstring string-view


【解决方案1】:

std::string_view 只不过是std::basic_string_view&lt;char&gt;,所以让我们看看它是documentation on cppreference

类模板basic_string_view 描述了一个对象,该对象可以引用char 类对象的恒定连续序列,序列的第一个元素位于零位。

一个典型的实现只包含两个成员:一个指向常量CharT的指针和一个大小。

突出显示的部分告诉我们为什么clang对std::string_view view(std::string{"hello, world"});是正确的:正如其他人评论的那样,因为声明完成后,std::string{"hello, world"}被销毁std::string_view 持有的底层指针悬空。

显然这只是一个典型的实现,但既然我们知道它是正确的,它至少告诉我们该标准不需要任何实现来做一些特殊的事情来保持临时工的生命。 p>

【讨论】:

    【解决方案2】:

    some_function(std::string{"hello, world"}); 是完全安全的,只要函数不保留 string_view 供以后使用。

    临时的std::string在这个完整表达式的末尾被销毁(粗略地说,在这个;处),所以在函数返回后被销毁。


    std::string_view view(std::string{"hello, world"}); 总是产生一个悬空的string_view,不管你是使用() 还是{}。如果括号的选择影响了编译器警告,那就是编译器缺陷。

    【讨论】:

      【解决方案3】:

      std::string 临时传递给std::string_view 参数是否安全?

      一般来说,它不一定是安全的。这取决于函数的作用。如果你不知道,那么你不应该认为它是安全的。

      知道如图所示的函数定义,使用临时字符串调用示例函数是安全的。

      some_function 内部的视图是否会引用已被销毁的字符串?

      在这种情况下不是,因为临时参数字符串 - 字符串视图引用的 - 尚未被销毁。


      有什么区别?

      函数参数的生命周期比作为参数传递的临时参数的生命周期短。字符串视图变量的生命周期比传递给构造函数的临时参数的生命周期长。

      【讨论】:

        【解决方案4】:

        正如其他人所说,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 值引用 非常有用。

        【讨论】:

        • 我想过,但我认为string_view 太小了,移动它不会有太大的不同。我认为能够愉快地复制string_views 是他们的重点。毕竟sizeof(string_view) 只返回 16 给我。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-02
        • 2017-05-19
        • 1970-01-01
        相关资源
        最近更新 更多