为什么 std::string 没有隐式转换为布尔值

why std::string is not implicitly converted to bool

本文关键字:转换 布尔值 std string 为什么      更新时间:2023-10-16

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

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

与其他语言一样(例如 Python)。我认为使用empty方法很乏味。

现在 C++11 添加了显式转换和上下文转换的概念,因此可以添加这一点。

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

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

像这样的事情经常发生,有很多早期的字符串类型尝试(而且有很多),委员会显然决定最好将隐式转换保持在绝对最小值(因此string支持的唯一隐式转换是从 C 样式字符串创建字符串对象)。

设计了许多方法来更安全地处理转换为布尔值的方法。一种是转换为void *,这可以防止一些问题,但不能防止其他问题(iostreams使用)。还有一个"安全布尔"成语(实际上,更像是一个"安全布尔"主题,其中有几个变体)。虽然这些肯定改善了对允许和不允许的转换的控制,但其中大多数都涉及相当多的开销(典型的安全布尔值需要~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是多么奇怪(在发生这种情况的语言中)。

本文提到了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";
}

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())并不难(恕我直言,它更清晰)。

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

您可以在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快。

如果有人发现了一种方法来做if (s),C++,请告诉我们。