返回类型时c_str()vs.data()

时间:2018-11-27 13:03:23

标签: c++ string c++17 c-str

在C ++ 11之后,我想到了127.0.0.1final class CustomTextView: NSTextView { private var placeholderAttributedString: NSAttributedString? = NSAttributedString(string: "Your placeholder string here") private var placeholderInsets = NSEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0) override func becomeFirstResponder() -> Bool { self.needsDisplay = true return super.becomeFirstResponder() } override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) guard string.isEmpty else { return } placeholderAttributedString?.draw(in: dirtyRect.insetBy(placeholderInsets)) } } extension NSRect { func insetBy(_ insets: NSEdgeInsets) -> NSRect { return insetBy(dx: insets.left + insets.right, dy: insets.top + insets.bottom) .applying(CGAffineTransform(translationX: insets.left - insets.right, y: insets.top - insets.bottom)) } } equivalently

C ++ 17为后者引入了一个重载,该重载返回了一个非恒定指针(reference,我不确定它是否在C ++ 17以后被完全更新了):

c_str()

c_str()只返回常量指针:

data()

为什么在C ++ 17中区分这两种方法,尤其是当C ++ 11是使它们同质的一种时?换句话说,为什么只有一个方法过载,而另一个却没有?

4 个答案:

答案 0 :(得分:22)

P0272R1为C ++ 17添加了新的重载。本文本身和其中的链接都没有讨论为什么只给data赋予了新的重载,而没有给c_str赋予了新的重载。在这一点上,我们只能推测(除非参与讨论的人参与其中),但我想提供以下几点供考虑:

  • 即使只是将重载添加到data也会破坏一些代码;保持这种变化保守是减少负面影响的一种方法。

  • 到目前为止,c_str函数与data完全相同,实际上是一种“传统”功能,用于连接采用“ C字符串”的代码,即不可变< / em>,以null终止的char数组。由于您始终可以将c_str替换为data,因此没有特别的理由要添加到该旧界面。

我意识到P0292R1的真正动机是,确实存在遗留的API,这些API错误地或出于C的原因即使不发生突变也仅采用可变的指针。一样,我想我们不想在字符串已经非常必要的大量API中添加更多。

还有一点:从C ++ 17开始,只要您将值写为零,您现在就allowed to write到空终止符。 (以前,它曾经是UB,可以向空终止符写入任何内容。)可变的c_str会为这种特殊的微妙之处创造另一个切入点,而且微妙之处越少越好。

答案 1 :(得分:21)

data()成员超载的原因在open-std.org的this论文中得到了解释。

论文的TL;博士论文:为.data()添加了非常量std::string成员函数,以提高标准库中的统一性并帮助C ++开发人员正确编写码。调用在C字符串参数中没有const限定的C库函数时,这也很方便。

论文中的一些相关段落:

  

摘要
  std::string缺少非常量.data()成员函数是基于C ++ 11以前的std::string语义的疏忽还是有意设计?无论哪种情况,这种功能的缺乏都会诱使开发人员在几种合法的情况下使用不安全的替代方案。本文主张为std :: string添加一个非常量.data()成员函数,以提高标准库中的统一性,并帮助C ++开发人员编写正确的代码。

     

用例
  C库有时会包含具有char *参数的例程。一个示例是Windows API中lpCommandLine函数的CreateProcess参数。因为data()的{​​{1}}成员是const,所以它不能用于使std :: string对象与std::string参数一起使用。像下面的示例一样,开发人员很想使用lpCommandLine

.front()
     

请注意,当std::string programName; // ... if( CreateProcess( NULL, &programName.front(), /* etc. */ ) ) { // etc. } else { // handle error } 为空时,programName表达式会导致未定义的行为。临时的空C字符串可修复该错误。

programName.front()
     

如果存在一个非常量std::string programName; // ... if( !programName.empty() ) { char emptyString[] = {'\0'}; if( CreateProcess( NULL, programName.empty() ? emptyString : &programName.front(), /* etc. */ ) ) { // etc. } else { // handle error } } 成员(如.data()一样),那么正确的代码将很简单。

std::vector
     

当调用在C字符串参数上没有const限定的C库函数时,非const std::string programName; // ... if( !programName.empty() ) { char emptyString[] = {'\0'}; if( CreateProcess( NULL, programName.data(), /* etc. */ ) ) { // etc. } else { // handle error } } 成员函数也很方便。这在较旧的代码以及需要与较旧的C编译器一起移植的代码中很常见。

答案 2 :(得分:5)

这仅取决于“您想使用它做什么”的语义。一般而言,async有时被用作缓冲向量,即代替std::string。通常可以在std::vector<char>中看到。换句话说,它是一个字符数组。

boost::asio:严格来说,您要查找的是一个以空字符结尾的字符串。从这种意义上讲,您永远不要修改数据,也永远不需要将字符串作为非常量。

c_str():您可能需要字符串中的信息作为缓冲区数据,甚至是非const。只要不涉及更改字符串的长度,您就可以修改数据,也可以不修改。

答案 3 :(得分:3)

由于std :: string类的历史,存在std :: string的两个成员函数 c_str data

直到C ++ 11,std :: string可能已实现为写时复制。内部表示形式不需要存储的字符串的任何空终止。成员函数 c_str 确保返回的字符串以null结尾。成员函数 data simlpy返回了指向存储字符串的指针,该指针不一定以null终止。 -为确保已注意到对字符串的更改以启用写时复制功能,这两个函数都需要返回指向const数据的指针。

当std :: string不再允许写时复制时,这一切在C ++ 11中都得到了改变。由于仍然需要 c_str 来传递以null结尾的字符串,因此,总是将null附加到实际存储的字符串中。否则,调用 c_str 可能需要更改存储的数据,以使字符串以null终止,这将使 c_str 成为非常量函数。由于 data 提供了指向存储字符串的指针,因此它通常具有与 c_str 相同的实现。由于向后兼容,这两个功能仍然存在。