【问题标题】:Why doesn't std::string provide implicit conversion to char*?为什么 std::string 不提供到 char* 的隐式转换?
【发布时间】:2010-10-04 06:31:59
【问题描述】:

std::string 提供const char* c_str ( ) const 其中:

获取等效的 C 字符串

生成一个空终止序列 相同的字符(c-string) 内容作为字符串对象和 将其作为指向数组的指针返回 字符。

终止空字符是 自动附加。

返回的数组指向一个 内部位置与所需 这个序列的存储空间 字符加上它的终止 空字符,但这个值 数组不应该被修改 程序,并且只被授予保留 直到下一次调用 a 的非常量成员函数 字符串对象。

他们为什么不直接定义operator const char*() const {return c_str();}

【问题讨论】:

  • 要记住的一件事是c_str() 只是将一个ptr 返回到std::string 的内部缓冲区。因此,返回字符串的生命周期与std::string 的生命周期相关联。强制程序员使用c_str() 方法有助于(至少对我而言)记住这一点。

标签: c++ string stl


【解决方案1】:

来自 C++ 编程语言 20.3.7(重点是我的):

可以通过操作符 const char*() 而不是 c_str() 转换为 C 风格的字符串。这将提供隐式转换的便利在这种转换意外的情况下以意外为代价

【讨论】:

  • 愿意解释一下代价高昂的惊喜是什么?
  • 1.当转换为 C 字符串时会导致内存重新分配,因为 std::string 不一定存储 NULL 终止符 2. 当 std::string 超出范围并且您留下一个指向已释放内存的悬空指针时
  • 我认为简短的回答是解决重载问题。此外,std::string 可以包含一个 \0 字符,这并不意味着 std::string 的结尾。
  • 这是一个重载问题。 “char *”的行为不像任何明智的人所期望的那样,将“string_1 - string_2”或“string_1 - 2”这样的表达式标记为编译器错误而不是编译成废话是很有用的。
  • @user7116 嵌入的\0 的问题也会影响.c_str(),所以强制使用它并不能让它变得更好。
【解决方案2】:

我发现隐式转换至少有两个问题:

  • 即使c_str() 提供的显式转换本身也足够危险。我见过很多情况,其中存储了指针以在原始字符串对象的生命周期结束后使用(或者对象被修改,从而使指针无效)。通过对c_str() 的明确调用,您希望能够意识到这些问题。但是使用隐式转换很容易导致未定义的行为,如下所示:

    const char *filename = string("/tmp/") + name;
    ofstream tmpfile(filename); // UB
  • 这种转换也会发生在某些你意想不到的情况下,而且语义至少可以说是令人惊讶的:

    string name;
    if (name) // always true
     ;
    name-2; // pointer arithmetic + UB
    这些可以通过某种方式避免,但为什么要首先陷入这个麻烦呢?

【讨论】:

  • +1 用于提供具体示例。任何人都可以说“哦,这很危险”,但没有例子就很难理解。
  • 这不是一个很好的例子,人们现在尝试像你的第一个例子一样编写代码,遇到编译器错误,并在 .c_str() 上标记到 string(...) 调用的末尾并在同样的情况。
【解决方案3】:

因为隐式转换几乎永远不会像您期望的那样运行。它们可以在重载解析中给出令人惊讶的结果,因此通常最好像 std::string 那样提供显式转换。

【讨论】:

    【解决方案4】:

    Josuttis 的书说:

    这是出于安全原因,以防止导致奇怪行为(char * 类型通常具有奇怪行为)和歧义(例如,在组合 string 和 C 字符串的表达式中)的意外类型转换可以将string 转换为char *,反之亦然。

    【讨论】:

    • 我只是输入了完全相同的引用:)
    【解决方案5】:

    除了规范中提供的基本原理(意想不到的惊喜),如果您将 C API 调用与std::string 混合使用,您确实需要养成使用::c_str() 方法的习惯。如果您曾经调用需要const char* 的可变参数函数(例如:printf 或等效函数),并且您直接传递了std::string(不调用提取方法),您将不会收到编译错误(否varargs 函数的类型检查),但您会收到运行时错误(类布局与 const char* 的二进制不同)。

    顺便说一句,CString(在 MFC 中)采用相反的方法:它具有隐式强制转换,并且类布局与const char*(或const w_char*,如果编译为宽字符串,即: “Unicode”)。

    【讨论】:

    • 我使用 g++,它总是将传递任何类型的对象标记为带有警告和消息的可变参数:如果这段代码运行,程序将意外终止。标准中没有强制要求,但大多数编译器应该标记它
    • 我一直很好奇 CString 是如何逃脱惩罚的 - 感谢您的洞察力。
    【解决方案6】:

    这可能是因为这种转换具有令人惊讶和特殊的语义。特别是你引用的第四段。

    另一个原因是存在隐式转换 const char* -> string,而这恰恰相反,这意味着在重载解决方案中会出现奇怪的行为(您不应该同时进行隐式转换 A->BB->A)。

    【讨论】:

    • 我知道专家们知道他们在做什么,但令我震惊的是,他们会让字符串类在没有充分理由的情况下如此难以使用。
    • 我不会说“这么难用”。但如果你觉得输入 7 个字符很困难,那么……
    • 如果您发现自己经常转换为 char*,您可能需要重新考虑您的设计。
    • @rmeador: 如果 C++ 标准库 接受 std::string 文件名,避免转换为 char* 会更容易...
    • @Porculus:不幸的是,这需要流标头包含字符串标头,这会稍微减慢某些翻译单元的编译速度。就我个人而言,我怀疑有人会注意到,即使是在企业级的千个应用程序自动化构建中,但也许我错了,有人衡量了差异。
    【解决方案7】:

    因为 C 风格的字符串是错误和许多安全问题的来源,所以最好明确地进行转换。

    【讨论】:

    • 这里无关。无论如何,张贴者显然会使用 C 风格的字符串。
    猜你喜欢
    • 1970-01-01
    • 2021-06-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-07
    • 2018-08-23
    • 1970-01-01
    • 2010-11-14
    相关资源
    最近更新 更多