85

有一个从std::stringto的隐式转换std::string_view,它不被认为是不安全的,即使如果程序员不小心,这肯定会导致很多悬空引用。

另一方面,没有从使用相同参数的隐式转换,std::string_view而是std::string以完全相反的方式:因为程序员可能不小心

很高兴 C++ 可以替代原始const char*指针,同时让它变得超级混乱并被剥离到骨头上:

  • 隐式const char*-> std::string好的
  • 隐式std::string_view-> std::string
  • 分配std::string= const char*好的
  • 分配std::string= std::string_view好的
  • 附加std::string+= const char*好的
  • 附加std::string+= std::string_view好的
  • 串联const char*+ std::string好的
  • 串联std::string_view+ std::stringNOPE
  • 串联std::string+ const char*好的
  • 串联std::string+ std::string_viewNOPE

我错过了什么或者这完全是胡说八道吗?

最后,如果没有所有使其类似于的关键部分,这个字符串视图有多大用处const char*?将它集成到stdlib的生态系统中而不是完成最后一步有什么意义?毕竟,如果我们需要一个代表一段字符串的对象,我们可以自己编写。实际上,很多图书馆早在几年前就已经这样做了。制定标准的全部意义在于使其适用于最广泛的用例,不是吗?

他们会在C++23中解决这个问题吗?

4

2 回答 2

39

问题是std::string_view->复制std::string了底层内存,完成了堆分配,而隐式->没有。如果您一开始就费心使用 a ,那么您显然会关心副本,因此您不希望隐式发生。std::stringstd::string_viewstd::string_view

考虑这个例子:

void foo1(const std::string& x)
{
    foo2(x);
}
void foo2(std::string_view x)
{
    foo3(x);
}
void foo3(const std::string& x)
{
    // Use x...
}

该函数foo2可以使用const std::string&参数,但使用了 a std::string_view,因此如果您传入不是 a 的字符串,它会更有效std::string。那里没有惊喜。但它的效率低于你只给它一个const std::string&参数!

  • 使用参数foo2调用时std::string(例如 by foo1):foo2调用时foo3,它会创建字符串的副本。如果它有一个const std::string&参数,它可以使用它已经拥有的对象。
  • 何时foo2使用const char*参数调用:std::string迟早必须制作副本;有一个const std::string&参数,它是更早制作的,但总的来说,无论哪种方式,都只有一个副本。

现在想象foo2调用多个函数,例如foo3,或foo3循环调用;std::string它一遍又一遍地制造完全相同的物体。您希望编译器通知您这一点。

于 2017-11-28T07:26:15.930 回答
9

因为昂贵的隐式转换是不可取的......

您只列出了整组示例中的一个const char*隐式转换: -> std::string; 你要另一个。在所有其他“OK”标记的函数中 - 内存分配是显而易见的/明确的:

  • 分配:当您将任何内容分配给具有可变存储大小的拥有对象时,可以理解可能需要分配。(除非这是一个移动任务,但没关系。)
  • 追加 I:当您追加到具有可变存储大小的拥有对象时,更明显的是它需要分配以容纳额外的数据。(它可能有足够的保留空间,但没有人保证关于字符串的这一点。)
  • 连接:在所有三种情况下,内存只分配给结果而不是任何中间对象。结果的分配显然是必要的,因为连接的操作数保持不变(并且不能假定无论如何都保存结果)。

隐式转换通常有利有弊。C++ 实际上对这些很吝啬,并且从 C 继承了大多数隐式转换。但是 - const char*tostd::string是一个例外。正如@ArthurTacca 所说,它分配内存。现在,C++ 核心指南

C.164: 避免隐式转换运算符

原因
隐式转换可能是必不可少的(例如,从 double 到 int)但经常会引起意外(例如,从 String 到 C 风格的字符串)。

当意外转换执行昂贵的操作(如分配)时,这种情况会加倍,这会产生副作用,并且可能会进行系统调用。


PS -std::string_view有很多陷阱;请参阅 Victor Ciura 的 CppCon 2018 演讲:

足以吊死string_view自己

所以请记住,它不是某种万能的灵丹妙药;这是另一个你需要小心使用的类,而不是粗心大意。

...显式构造函数就足够了。

实际上确实存在fromstd::string的显式构造函数std::string_view。使用它 - 通过传递std::string{my_string_view}给带字符串的函数。

于 2020-06-19T20:55:06.940 回答