非void函数可能缺少返回

Non-void function potential lack of return

本文关键字:返回 void 函数      更新时间:2023-10-16

我写了这段代码来检查节点是树中的左子节点还是右子节点。然而,从清洁的角度来看,我认为它没有得到适当的实施。

我的问题是,如果父指针为null,则返回类似false的默认值是危险的,因为false意味着它是一个正确的子指针。但是,如果我想在没有警告的情况下编译此函数,则需要返回。作为一种解决方案,空投绝对是丑陋的。

我见过人们使用assert(parent)而不是if,但我认为这也不好,因为断言调试功能。

简而言之,有什么更好的想法来实现这一功能?

bool isLeftChild() const {
if(parent){
if(this == (parent->left).get()){
return true;
}
else if(this == (parent->right).get()){
return false;
}
}
else throw;
}

清洁度最终取决于意见。您遇到的问题是这个函数的约定是什么。也就是说,如果在父节点为NULL的节点上调用该代码,您希望它定义该代码的行为吗?

如果答案是肯定的,那么投掷是完全有效的。很像vector::at在索引越界时抛出。

bool isLeftChild() const
{
if(!parent) throw ...;
if(this == (parent->left).get()){
return true;
}
else if(this == (parent->right).get()){
return false;
}
}

如果答案是否定的,那么你就不应该扔。这就是vector::operator[]处理越界索引的方式。在C++20中,您可以将其表示为一个契约:

bool isLeftChild() const
[[expects: parent != nullptr]]
{
if(this == (parent->left).get()){
return true;
}
else if(this == (parent->right).get()){
return false;
}
}

但这在概念上是一种断言;如果在父节点为NULL的节点上调用此函数,则会导致未定义的行为。

哪一个"更干净"取决于你自己以及你希望你的界面如何表现。"宽合约"(你抛出失败而不是调用UB(通常被视为"更安全"的替代方案。这主要是因为你会从出错的地方抛出异常,而不是从其他地方抛出。但在这种特殊情况下,如果parent为NULL,当您尝试取消引用它时,您会立即发现

但即便如此,在现代C++中,宽契约通常被认为是糟糕的形式。事实上,对于C++20中的合同,委员会正在考虑慢慢删除标准库中抛出logic_error的位置,将它们变成违反合同的地方(即:UB(。

我想这取决于你想要什么。我认为,如果节点不是任何人的孩子,那么一个名为isLeftChild()的函数应该返回false,因为如果它不是任何人,那么它肯定不是左孩子…

关于assert()的使用,您应该问自己一个问题:在函数被错误调用的情况下,是否有必要保证定义良好的行为?assert()的目的是捕捉在正确的程序中不应该出现的情况。一旦构建了发布版本,这些检查就不必再进行了,因为它们检查的情况可能(应该(永远不会发生。另外,请注意,除非正在处理活动异常,否则不带参数的throw表达式将简单地调用std::terminate()。我怀疑isLeftChild()是否打算仅从catch块内部独占调用!?因此,为了让这个throw做你最可能想做的事情,你需要抛出一些的东西,例如std::logic_error