【发布时间】:2009-08-25 22:05:04
【问题描述】:
我在coding standards sheet 中发现了以下规则:
不要依赖条件中的隐式转换为布尔值。
if (ptr) // 错误
if (ptr != NULL) // 没问题
这条规则有多合理/有用?
编译后的代码有多少重载?
【问题讨论】:
标签: c++ coding-style implicit-conversion
我在coding standards sheet 中发现了以下规则:
不要依赖条件中的隐式转换为布尔值。
if (ptr) // 错误
if (ptr != NULL) // 没问题
这条规则有多合理/有用?
编译后的代码有多少重载?
【问题讨论】:
标签: c++ coding-style implicit-conversion
从最严格的意义上说,您可以依赖隐式转换为 bool。向后兼容 C 需要它。
因此它变成了代码可读性的问题。通常,代码标准的目的是强制与代码风格保持一致,无论您是否同意这种风格。如果您正在查看其他人的标准并想知道是否应该将其纳入自己的标准,请继续讨论它 - 但如果这是您公司的长期规则,请学会接受它。
附:我刚加入了一个有同样规则的组织。我实际上对此很好,因为我一直认为显式优于隐式。我不能忍受的一件事是bool condition; ... if (condition == true),它太多余了,以至于我的眼睛都感到刺痛。
无论检查是隐式还是显式,任何体面的编译器都应该生成相同的代码,因此不应该考虑。
【讨论】:
bool,就像你可以依赖运算符优先级一样,即使编译器建议你放入多余的括号(对于这两种情况,它认为规则的概率为零将永远改变)。您甚至可以依靠隐式转换为 bool 来咬您,每当您忘记取消引用某个位置的指针时,由于某种原因(例如重载 bool 值也会有意义)。
如果您想使用隐式转换为bool 的类,例如std::istream,那么该规则会让您陷入困境。此代码从文件中一次读取一个单词,直到到达 EOF:
std::ifstream file("foo.txt");
std::string word;
while (file >> word)
{
// do stuff
}
流提取运算符返回对文件流的引用,该引用被隐式转换为bool,以指示流是否仍处于良好状态。当您到达文件末尾时,测试失败。您的编码标准使您无法使用这种通用结构。
对于指针类型,这没什么大不了的。编译器可能会为隐式转换为bool 和针对NULL 的显式测试生成大致相同的代码。在这一点上,这是一个品味问题——从绝对意义上说,没有一个人“更好”。编码标准只是试图强制执行一致的样式。
考虑到这一点,在处理内置类型(指针、整数等)时,您绝对应该遵循编码标准。如果您遇到与上述类似的情况,并且课程合法转换为 bool,我会向您的队友提出这个问题。
【讨论】:
while(static_cast<bool>(file>>word))。在这一点上,风格指南作者可能想要改写规则:“不要依赖 default 在条件中从指针到 bool 的隐式转换”,这可能是最初基于给出的例子。或者他可能想要求static_cast 在那里,以提醒读者发生了转换。您无需使用隐式转换即可使用转换。
在大多数情况下,这并不可怕,但如果您准确输入您的意思,它可以更具可读性。
【讨论】:
我想为这个问题添加一个历史观点。
ANSI C(又名 C89/C90)是 C 的第一个正式规范。除了K&R C,您可能会注意到一些特殊的东西——没有布尔值的概念。控制流语句对 表达式 进行操作,它们的流程是根据表达式的计算结果是否为 0 来定义的。缺少布尔类型是 C 的最大疏忽之一。直到 C99,C 才获得_Bool 类型,并在stdbool.h 中为bool、true 和false 定义了宏(注意它们仍然是整数)。同样,C++ 最初也没有布尔类型,在 C++98 和 bool 中获得它。
这就是为什么隐式转换为布尔值的原因。在我看来,继续依赖它是糟糕的设计——添加布尔值是有原因的。 true 应该始终等于 true,并且将所有非零值隐式转换为 true 是不令人满意的。
NULL 也只是一个等于 0 的宏,所以具体来说,对于 C++11 中的指针,您应该使用 nullptr 来确定指针是否为空。
if(ptr != nullptr)
{
// Do something with ptr.
}
【讨论】:
有时我认为这条规则可以被打破,例如处理返回 int 而不是 bool 的标准库(为了与 C89 兼容)。
但是,此规则通常会导致代码更易于理解。它甚至在 C# 之类的语言中也得到了强制执行,并且在那里并没有太多抱怨,所以除了必须习惯它之外,遵循这条规则并没有什么大的缺点。
【讨论】:
int 的东西都应该与 0 进行比较,以便让代码的读者清楚地知道发生了什么。例如if(someAwfulCStyleFunction() != 0) { /*error handling code*/ } 清楚地暗示“这个函数返回一个int 并且一个非零值代表一个错误”。
这根本不会影响编译后的代码。
至于它有多大用处 - 它肯定有助于理解来自 Java/C# 等语言的人。它会使您正在检查的内容更加明确。如果您开始将 int 与 NULL 进行比较,它将引发警告(从而表明您对所讨论变量的类型不清楚)。我个人更喜欢第一种形式,但这并不是一个完全不合理的要求。
【讨论】:
我非常不喜欢这条规则。在 C++ 中,对于指针类型(当然对于布尔类型)使用到 bool 的隐式转换是惯用的。 IMO,它更容易阅读
bool conditionMet = false;
while (!conditionMet) // read as "while condition is not met"
{
/* do something */
}
比读这个:
bool conditionMet = false;
while (conditionMet == false) // read as "while conditionMet is false"
{
/* do something */
}
指针也一样。此外,通过引入不必要的比较,您又引入了另一个错误输入的机会,并以分配而不是比较结束,这当然会产生不希望的结果。如果您使用整数作为布尔值,就像旧的 C 代码一样,我认为您还应该使用到布尔值的隐式转换。
【讨论】:
bool 类型,它不会强制执行任何操作(尽管编译器可能会或可能不会做)。对于指针类型,它有点灰色。对于其他任何事情,您都应该使用比较运算符。
增加零收益的规则是您可以有利可图地删除的规则。
【讨论】:
这是一个非常愚蠢的规则。
if (ptr != NULL) // ok
那为什么不呢
if ((ptr != NULL)==true)
【讨论】: