【问题标题】:why std::string is not implicitly converted to bool为什么 std::string 没有隐式转换为 bool
【发布时间】:2015-01-30 00:00:06
【问题描述】:

c++ 中的std::string 没有隐式转换为布尔值有什么原因吗?例如

std::string s = ""
if (s) { /* s in not empty */ }

与其他语言(例如 python)一样。我觉得用empty这个方法很繁琐。

【问题讨论】:

  • 因为它没有运算符bool(),因为标准不需要一个,因为......应该如何定义?为什么你认为空为假,非空为真?
  • 它只是不是(空向量也不会评估为假,等等)。也许是疏忽,但由于已经出现大量隐式转换错误,因此可能故意省略。
  • @deviantfan:这不是我的意见:它在许多语言中都很常见
  • @RuggeroTurra 但是 C++ 不像 Python、JS 和 co。如果您根据 asm 中的字符串(跳转等)创建条件,那么您得到的最佳结果每次都是正确的,因为地址不是 0。这就是 C 和 C++ 的起源,而不是动态类型等。跨度>
  • 这可能是许多语言的共同特征,但在那些语言中肯定没有一个共同的约定,字符串值应该被认为是假的,哪些应该被认为是真的。

标签: c++ string casting boolean


【解决方案1】:

这可能可以添加,因为 C++11 已经添加了显式转换和上下文转换的概念。

在设计std::string 时,这些都不存在。这使得支持转换为bool 的类很难保证安全。特别是,这种转换可能(并且将会)发生在很多你几乎不希望发生的情况下。例如,如果我们假设std::string 为空则转换为false,否则转换为true,那么您可以使用string,基本上任何地方都是打算使用整数或指针。

编译器不会告诉你类型不匹配,而是将字符串转换为 bool,然后将 bool 转换为整数(false -> 0,true -> 1)。

这样的事情经常发生,在字符串类型的许多早期尝试中(并且有很多)委员会显然认为最好将隐式转换保持在绝对最低限度(所以关于唯一的string 支持的隐式转换是从 C 风格的字符串创建字符串对象。

设计了许多方法来更安全地处理转换为布尔值。一个是转换为void *,这可以避免一些问题,但不能避免其他问题(iostreams 使用了这个)。还有一个“安全布尔”成语(实际上,更像是一个“安全布尔”主题,其中有几个变体)。虽然这些确实改进了对允许和不允许转换的控制,但它们中的大多数都涉及相当多的开销(典型的安全 bool 需要约 50 行代码的基类,加上从该基类派生等。 )

至于显式转换和上下文转换有什么帮助,基本思想非常简单。您可以(从 C++11 开始)将转换函数标记为 explicit,这允许它仅在使用显式转换为目标类型的情况下使用:

struct X {
    explicit operator bool() { return true; }
};

int main() { 
    X x;
    bool b1 = static_cast<bool>(x); // compiles
    bool b2 = x;   // won't compile
}

上下文转换增加了一点,让转换为 bool 隐式发生,但 在类似于 if 语句的情况下,因此使用具有上述转换函数的类,您会得到:

X x;
if (x) // allowed

int y = x; // would require explicit cast to compile

我要补充一点,关于“正交性”的抱怨在这里似乎完全不适用。尽管方便,将字符串转换为布尔值并没有多大意义。如果有的话,我们应该抱怨string("0") 转换为1 是多么奇怪(在发生这种情况的语言中)。

【讨论】:

    【解决方案2】:

    This article 提到了为什么operator bool() 会导致令人惊讶的结果的一些原因。

    注意std::string 只是std::basic_string&lt;char&gt; 的类型定义。还有std::wstring 用于多字节字符。隐式转换可以让你写:

    std::string foo = "foo";
    std::wstring bar = "bar";
    if (foo == bar) {
      std::cout << "This will be printed, because both are true!\n";
    }
    

    【讨论】:

      【解决方案3】:

      std::string 仍然必须与 C 风格的字符串共存。

      根据定义,C 风格的字符串是“由第一个空字符终止并包含第一个空字符的连续字符序列”,通常通过指向其第一个字符的指针访问。在大多数情况下,"hello, world" 之类的表达式会隐式转换为指向第一个字符的指针。然后可以将这样的指针隐式转换为bool,如果指针非空则产生true,如果指针为空则产生false。 (在C语言中没有转换成bool,但还是可以直接作为条件使用,所以效果差不多。)

      所以,由于 C++ 的 C 传统,如果你写:

      if ("") { ... }
      

      空字符串已被视为true,如果不破坏 C 兼容性,就无法轻易更改。

      我建议让 C 样式的空字符串评估为 true 和 C++ 空的 std::string 评估为 false 会太混乱。

      if (!s.empty()) 并不是那么困难(恕我直言,它更易读)。

      【讨论】:

        【解决方案4】:

        我能找到的最接近您(和我)想要的东西是以下内容。

        您可以像这样在std::string's 上定义! 运算符:

        bool operator!(const std::string& s)
        {
            return s.empty();
        }
        

        这允许你做:

        std::string s;
        if (!s)             // if |s| is empty
        

        使用简单的否定,您可以:

        if (!!s)            // if |s| is not empty
        

        这有点尴尬,但问题是,你想避免多余的字符有多严重? !strcmp(...) 也很尴尬,但我们仍然可以正常工作并习惯了它,我们中的许多人更喜欢它,因为它比输入 strcmp(...) == 0 更快。

        如果有人发现用 C++ 处理 if (s) 的方法,请告诉我们。

        【讨论】:

          猜你喜欢
          • 2021-06-01
          • 1970-01-01
          • 2017-10-20
          • 2013-09-07
          • 2012-12-02
          • 2010-10-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多