首页 > 解决方案 > 为什么 C++ 允许实际上不返回值的函数?

问题描述

在 C++ 中,允许使用没有 return 语句的非 void 返回类型的函数。因此,以下代码将编译:

std::string give_me_a_string()
{
}

但是,在 C# 中,不允许使用这种方法。因此,以下代码将无法编译:

public string GiveMeAString()
{
}

为什么会这样?这两种语言的设计原理是什么?

标签: c#c++language-design

解决方案


C++ 要求代码“表现良好”以便以定义的方式执行,但该语言不会尝试比程序员更聪明——当出现可能导致未定义行为的情况时,编译器可以自由地假设这种情况实际上永远不会在运行时发生,即使它不能通过其静态分析来证明。

从函数的末尾流出相当于没有值的返回;这会导致值返回函数中的未定义行为。

调用这样的函数是合法的行为;仅在不提供值的情况下从其末端流出是未定义的。我想说允许这样做是有正当理由(而且主要是遗留)的原因,例如,您可能正在调用一个总是抛出异常或执行的函数longjmp(或者有条件地这样做,但您知道它总是发生在这个地方,并且[[noreturn]]只来了在 C++11 中)。

虽然这是一把双刃剑,因为虽然在您知道不可能发生的情况下不必提供值可能有利于进一步优化代码,但您也可能错误地忽略它,类似于从未初始化的变量中读取。过去有很多这样的错误,所以这就是为什么现代编译器会警告你这个,有时还会插入一些守卫,使它在运行时有点易于管理。

例如,过度优化的编译器可能会假设从不产生其返回值的函数实际上永远不会返回,并且它可以继续这种推理直到创建一个空main方法而不是您的代码。


另一方面,C# 有不同的设计原则。它旨在编译为中间代码,而不是本机代码,因此其可定义性规则必须符合中间代码的规则。并且 CIL 必须是可验证的才能在某些地方执行,因此必须事先检测到诸如溢出函数结尾之类的情况。

C# 的另一个原则是在常见情况下不允许未定义的行为。由于它也比 C++ 更年轻,它的优势在于假设计算机足够高效,可以支持比 C++ 开始时更强大的静态分析。编译器可以负担得起检测这种情况,并且由于 CIL 必须是可验证的,因此只有两个操作是可行的:静默发出引发异常的代码(有点assert false),或者完全禁止这种情况。由于 C# 还具有从 C++ 课程中学习的优势,因此开发人员选择了后者。

这仍然有它的缺点——有一些辅助方法永远不会返回,并且仍然没有办法在语言中静态地表示它,所以你必须return default;在调用这些方法之后使用类似的东西,这可能会使任何阅读代码的人感到困惑.


推荐阅读