【问题标题】:When would I pass const& std::string instead of std::string_view?我什么时候会通过 const& std::string 而不是 std::string_view?
【发布时间】:2020-03-24 15:24:49
【问题描述】:

我了解使用std::string_view 的动机;
它可以帮助避免在函数参数中进行不必要的分配。

例如:
以下程序将从字符串文字创建 std::string
这会导致不希望的动态分配,因为我们只对观察字符感兴趣。

#include <iostream>

void* operator new(std::size_t n)
{
    std::cout << "[allocating " << n << " bytes]\n";
    return malloc(n);
}

void observe_string(std::string const& str){}

int main(){
  observe_string("hello world"); //prints [allocating 36 bytes]
}

使用string_view会解决问题:

#include <iostream>
#include <experimental/string_view>

void* operator new(std::size_t n)
{
    std::cout << "[allocating " << n << " bytes]\n";
    return malloc(n);
}

void observe_string(std::experimental::string_view const& str){
}

int main(){
  observe_string("hello world"); //prints nothing
}

这给我留下了一个问题。
我什么时候会选择 std::string by const& 而不是 string_view 作为函数参数?

查看std::string_view 的接口,看起来我可以替换const&amp; 传递的所有std::string 实例。这有什么反例吗? std::string_view 是不是要替换 std::string const&amp; 进行参数传递?

【问题讨论】:

  • 我想说:如果函数要将参数隐藏到内部离散的 std::string 中,那么复制构造将比 string-to-string-view-to 便宜-字符串往返。但我意识到 basic_string_view 可以记住它是由一个字符串构造的,并从它的 to_string() 方法返回相同的。
  • @SamVarshavchik:如果您要制作副本,您希望按值接受参数,以便可以复制或移动构造。
  • 如果您要将参数传递给只接受字符串的东西,例如ifstream 的构造函数或operator+(请参阅this question),那么您不妨采取它是const string &amp;。 (我想这只是一种复杂的表达方式“当你真的需要string。)

标签: c++ string stl parameter-passing string-view


【解决方案1】:

我什么时候会选择 std::string by const&amp; 而不是 string_view 作为函数参数?

需要一个以空字符结尾的字符串吗?如果是这样,那么您应该使用std::string const&amp;,它可以为您提供保证。 string_view 没有 - 它只是 const char 的一个范围。

如果您不需要需要一个以空字符结尾的字符串,并且您不需要拥有数据的所有权,那么您应该使用string_view。如果您确实需要拥有数据的所有权,那么string 的价值可能优于string_view

【讨论】:

  • 为了澄清:需要我的字符串空终止意味着std::string底层char-array 应该以\0结尾,对吗?很难想象有人在使用std::string 时需要这个。这就像将 C 与现代 C++ 混合,还是我错了?
  • 如果需要一个NTBS,我想他们可能只是使用const char*作为参数类型。
  • @M.Winter 很容易想象...有很多 API 需要以空字符结尾的字符串。
【解决方案2】:

接受const std::string&amp; 而不是string_view 的一个可能原因是当您想要存储对稍后可以更改的字符串对象的引用时。

如果您接受并存储一个string_view,它可能会在string 内部缓冲区重新分配时变得无效。

如果您接受并存储对字符串本身的引用,则只要该对象处于活动状态,您就不会遇到该问题(您可能希望删除 r-value 引用重载,以避免临时对象出现明显问题)。

【讨论】:

    【解决方案3】:

    Andrei Alexandrescu 曾经说过,“没有工作胜过一些工作”。所以你应该在这种情况下使用const std::string&amp;。因为std::string_view还涉及到一些工作(复制一对指针和长度)。

    当然,const 引用可能仍然需要复制指针的成本;这几乎等同于std::string_view 将做的事情。但是std::string_view 还有一项额外的工作,它还复制了长度。

    这是理论上的,但在实践中,将首选基准来推断性能


    【讨论】:

    • @M.M 假设我们在每次递归中查看的不是同一个切片?
    • @M.M,我已经删除了递归示例,它实际上支持 string_view 这不完全是 OP 的问题。
    • 如果用字符串字面量调用函数,const string&amp; 版本会做更多的工作。
    • 这并不重要,但对于 string_view 来说,它将是两个指针,或者一个指针和一个长度。所以工作量正好翻倍。更重要的是要考虑 16 个字节是否适合您的 CPU 寄存器以及任何其他参数(而不是单个指针的 8 个字节);而且,正如@juanchopanza 所说,用户总是会扫描他们的字符串。
    【解决方案4】:

    这并不是您真正要问的,但出于性能原因,有时您想采用std::string 按价值 而不是std::string_view。在这种情况下,您需要在检查字符串之前对其进行修改:

    bool matches(std::string s)
    {
      make_upper_case(s);
      return lib::test_if_matches(s);
    }
    

    无论如何你都需要一个可变字符串,所以你可以将它声明为函数参数。如果您将其更改为std::string_view,并且有人将std::string 传递给函数matches(),您将首先将string 转换为string_view,然后将string_view 转换为string,因此分配了两次。

    【讨论】:

      猜你喜欢
      • 2021-12-29
      • 1970-01-01
      • 1970-01-01
      • 2017-02-28
      • 1970-01-01
      • 2017-11-22
      • 2021-10-27
      相关资源
      最近更新 更多