为什么 std::string 没有隐式转换为 bool
why std::string is not implicitly converted to bool
为什么在 c++ 中 std::string
没有隐式转换为布尔值?例如
std::string s = ""
if (s) { /* s in not empty */ }
与其他语言一样(例如 python)。我认为使用 empty
方法很乏味。
这可能可以添加,因为 C++11 已经添加了显式转换和上下文转换的概念。
设计std::string
时,这些都不存在。这使得支持转换为 bool
的 classes 很难保持安全。特别是,在许多您几乎不希望发生的情况下,这种转换可能(并且将会)发生。例如,如果我们假设 std::string
转换为 false
如果为空,否则转换为 true
,那么您可以使用 string
本质上 anywhere 一个整数或指针。
编译器不会告诉您类型不匹配,而是会将字符串转换为布尔值,然后将布尔值转换为整数(假 -> 0,真 -> 1)。
像这样的事情经常发生,许多早期的字符串类型尝试(并且有 很多 )委员会显然决定最好将隐式转换保持在绝对最低限度(因此 string
支持的唯一隐式转换是从 C 风格字符串创建字符串对象)。
设计了多种方法来更安全地处理到 bool 的转换。一个是转换为 void *
,这可以防止一些问题,但不能防止其他问题(这被 iostreams 使用)。还有一个 "safe bool" 成语(实际上,更像是一个 "safe bool" 主题,其中有几个变体)。虽然这些确实改进了对允许和不允许的转换的控制,但它们中的大多数都涉及相当多的开销(典型的安全 bool 需要约 50 行代码的基础 class,加上从该基础派生class,等等)
至于显式转换和上下文转换有何帮助,基本思路非常简单。您可以(从 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
语句的东西中,因此使用 class使用上面的转换函数,你会得到:
X x;
if (x) // allowed
int y = x; // would require explicit cast to compile
我要补充一点,关于 "orthogonality" 的抱怨在这里似乎很不适用。尽管方便,但将字符串转换为布尔值并没有多大意义。如果有的话,我们应该抱怨 string("0")
转换为 1
是多么奇怪(在发生这种情况的语言中)。
This article 提到了 operator bool()
可以导致令人惊讶的结果的一些原因。
请注意 std::string
只是 std::basic_string<char>
的类型定义。还有 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 风格的字符串共存。
根据定义 "a contiguous sequence of characters terminated by and including the first null character",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
.
更快
如果有人发现用 C++ 实现 if (s)
的方法,请告诉我们。
为什么在 c++ 中 std::string
没有隐式转换为布尔值?例如
std::string s = ""
if (s) { /* s in not empty */ }
与其他语言一样(例如 python)。我认为使用 empty
方法很乏味。
这可能可以添加,因为 C++11 已经添加了显式转换和上下文转换的概念。
设计std::string
时,这些都不存在。这使得支持转换为 bool
的 classes 很难保持安全。特别是,在许多您几乎不希望发生的情况下,这种转换可能(并且将会)发生。例如,如果我们假设 std::string
转换为 false
如果为空,否则转换为 true
,那么您可以使用 string
本质上 anywhere 一个整数或指针。
编译器不会告诉您类型不匹配,而是会将字符串转换为布尔值,然后将布尔值转换为整数(假 -> 0,真 -> 1)。
像这样的事情经常发生,许多早期的字符串类型尝试(并且有 很多 )委员会显然决定最好将隐式转换保持在绝对最低限度(因此 string
支持的唯一隐式转换是从 C 风格字符串创建字符串对象)。
设计了多种方法来更安全地处理到 bool 的转换。一个是转换为 void *
,这可以防止一些问题,但不能防止其他问题(这被 iostreams 使用)。还有一个 "safe bool" 成语(实际上,更像是一个 "safe bool" 主题,其中有几个变体)。虽然这些确实改进了对允许和不允许的转换的控制,但它们中的大多数都涉及相当多的开销(典型的安全 bool 需要约 50 行代码的基础 class,加上从该基础派生class,等等)
至于显式转换和上下文转换有何帮助,基本思路非常简单。您可以(从 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
语句的东西中,因此使用 class使用上面的转换函数,你会得到:
X x;
if (x) // allowed
int y = x; // would require explicit cast to compile
我要补充一点,关于 "orthogonality" 的抱怨在这里似乎很不适用。尽管方便,但将字符串转换为布尔值并没有多大意义。如果有的话,我们应该抱怨 string("0")
转换为 1
是多么奇怪(在发生这种情况的语言中)。
This article 提到了 operator bool()
可以导致令人惊讶的结果的一些原因。
请注意 std::string
只是 std::basic_string<char>
的类型定义。还有 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 风格的字符串共存。
根据定义 "a contiguous sequence of characters terminated by and including the first null character",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
.
如果有人发现用 C++ 实现 if (s)
的方法,请告诉我们。