Non-void 潜在功能不足 return

Non-void function potential lack of return

我写了这段代码来检查一个节点是树中的左节点还是右节点 child。但是从清洁的角度来看,我认为它没有得到妥善实施。

我的问题是,如果 parent 指针为空,return 设置默认值如 false 是危险的,因为 false 意味着它是正确的 child。然而,如果我想让它在没有警告的情况下编译,这个函数需要一个 return 。作为解决方案,空投绝对是丑陋的。

我看到人们使用 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;
}

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

关于 assert() 的使用,您应该问自己一个问题:是否有必要保证 well-defined 在函数被错误调用的情况下的行为? assert() 的要点是捕捉正确程序中不应该出现的情况。一旦构建了发布版本,就不再需要这些检查了,因为它们检查的情况可能(应该)永远不会发生。另外,请注意,没有参数的 throw 表达式将简单地调用 std::terminate() ,除非正在处理活动异常。我怀疑 isLeftChild() 只能从 catch 块内部调用!?因此,要让这个 throw 执行您最有可能想要执行的操作,您需要抛出 某些内容 ,例如 std::logic_error.

清洁度终究是见仁见智。你遇到的问题是这个函数的契约是什么。也就是说,如果在具有 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 的节点上调用这个函数会导致未定义的行为。

哪个 "cleaner" 取决于您以及您希望界面如何运行。 "Wide contracts"(你抛出失败而不是调用 UB)通常被视为 "safer" 的替代方案。这主要是因为您会从出错的地方而不是其他位置抛出异常。但在这种特殊情况下,如果 parent 为 NULL,当您尝试取消引用它时,您会立即发现。

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