为什么std::string不能隐式转换为bool

22

在C++中,为什么std::string不能隐式转换为布尔值?例如:

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

就像其他编程语言(例如Python)一样,我认为使用empty方法很繁琐。


12
由于标准没有要求,所以它没有bool()操作符,那么它应该如何定义?你认为为空为假,非空为真的原因是什么? - deviantfan
5
这仅仅是因为空向量并不会被评估为假值,等等。也许这是一个疏忽,但也可能是故意省略的,因为已经存在很多隐式转换错误。 - Cameron
4
@deviantfan:这不是我的观点,这在许多语言中都很普遍。 - Ruggero Turra
2
@RuggeroTurra 但是C++不像Python、JS等语言。如果你在汇编中基于字符串进行条件判断(跳转等),那么最好的结果就是每次都为真,因为地址不为0。这就是C和C++的起源,而不是动态类型等等。 - deviantfan
5
在许多语言中,这可能是一个常见的特征,但对于哪些字符串值应被视为假、哪些应被视为真,这些语言之间肯定没有共同的约定。 - Benjamin Lindley
显示剩余5条评论
4个回答

31
现在C++11已经添加了显式转换和上下文转换的概念,因此可能可以添加这个功能。
当设计std :: string时,这两者都不存在。这使得支持转换为bool的类非常难以保证安全性。特别是,该转换可能(并且会)发生在几乎从未想要的情况下。例如,如果我们假设如果为空则std :: string将转换为false,否则将转换为true,则您可以使用string基本上在任何整数或指针所需的地方。
编译器不会告诉您类型不匹配,而是将字符串转换为bool,然后将bool转换为整数(false-> 0,true-> 1)。
许多早期的字符串类型尝试(而且有很多)都经常发生这种情况,以至于委员会显然决定尽量将隐式转换降至最低水平(因此,string支持的唯一隐式转换是从C样式字符串创建字符串对象)。
有许多方法可以处理更安全的转换为bool。其中之一是转换为void *,这可以防止某些问题,但不能解决其他问题(iostreams使用了这种方法)。还有一个“安全布尔”习惯用法(实际上更像是“安全布尔”主题,有几种变化)。虽然这些肯定提高了对允许哪些转换和不允许哪些转换的控制,但它们大多涉及相当多的开销(典型的安全布尔需要一个约50行代码的基类,以及从该基类派生等)。
至于显式转换和上下文转换如何帮助,基本思想非常简单。您可以(从C ++ 11开始)将转换函数标记为显式,这样它只能在使用到目标类型的显式转换时使用:
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 的情况是多么奇怪。


11

这篇文章提到了一些operator bool()会导致意想不到结果的原因。

请注意,std::string只是std::basic_string<char>的typedef。还有一个用于多字节字符的std::wstring。隐式转换可以让您编写:

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

6

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上定义!运算符:
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)的方法,请告诉我们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接