该程序使用 C++ 编译和运行,但不适用于许多不同的语言,如 Java 和 C#。
#include <iostream>
using namespace std;
void foo2() {
cout << "foo 2.\n";
}
void foo() {
return foo2();
}
int main() {
foo();
return 0;
}
在 Java 中,这会导致编译器错误,例如“Void 方法无法返回值”。但是由于被调用的方法本身就是一个空值,它不返回值。我知道为了可读性可能会禁止这样的构造。还有其他反对意见吗?
编辑:为了将来引用,我在这里发现了一些类似的问题 return-void-type-in-c-and-c
在我看来,这个问题还没有得到解答。
回答“因为它在规范中这么说,继续前进”并没有削减它,因为首先必须有人编写规范。也许我应该问“允许返回像 C++ 这样的 void 类型的利弊是什么”?
最佳答案
这是因为它可以在模板中使用。 C# 和 Java 禁止 void
作为类型参数,但 C++ 允许它允许您编写这样的模板代码:
template<typename T, typename TResult>
TResult foo(T x, T y)
{
return foo2(x, y);
}
如
void
方法不允许返回 void
表达式,如果 TResult
,这个模板实例化将是不可能的是 void
.如果是这种情况,如果您想要的话,您将需要一个单独的模板定义 TResult
实际上是 void
.例如,请记住在 C# 中有两组通用的通用委托(delegate),即
Func<>
和 Action<>
?那么,Action<T>
存在正是因为Func<T, void>
是禁止的。 C++ 设计者不想尽可能引入这样的情况,因此他们决定允许您使用 void
作为模板参数——您发现的案例正是促进这一点的功能。(请允许我以假装问答的形式写下其余部分。)
But why do C# and Java not allow a similar construct?
首先,了解如何在这些语言中实现泛型编程:
Why pick one approach of implementing generic programming over the other?
Okay, so what would C# and Java need to do to support
void
as a valid generic argument?
我不得不推测来回答这个问题,但我会尝试。
在语言层面,他们将不得不放弃
return;
的概念。仅在 void
中有效方法并且对于非 void
总是无效的方法。如果没有这个改变,很少有有用的方法可以被实例化——而且它们可能都必须以递归或无条件 throw
结束。 ( which satisfies both void
and non- void
methods without returning )。因此,要使其有用,C# 和 Java 还必须引入允许您返回 void
的 C++ 特性。表达式。好的,让我们假设你有这个,现在你可以写这样的代码:
void Foo2() { }
void Foo()
{
return Foo2();
}
同样,非泛型版本在 C# 和 Java 中与在 C++ 中一样无用。但是让我们继续看它真正的用处,那就是泛型。
您现在应该能够编写这样的通用代码 -- 和
TResult
现在可以是 void
(除了已经允许的所有其他类型):TResult Foo<T, TResult>(T a)
{
return Foo2(a);
}
但请记住,在 C# 和 Java 中,重载解析发生“早”,而不是“晚”。重载解析算法将为每个可能的
TResult
选择相同的被调用者.并且类型检查器将不得不提示,因为您要么返回 void
来自可能非 void
的表达式方法或者你返回一个非 void
来自可能 void
的表达式方法。换句话说,外部方法不能是通用的,除非:
What if we went with the first option - make the callee's return type generic and move on?
我们可以这样做,但它只是将我们的问题推给了被调用者。
在某些时候,我们需要某种方式来“实例化”某种
void
实例并可选择以某种方式接收它。所以现在我们需要 void
的构造函数(尽管每个 void
方法都可以算作工厂方法,如果你眯着眼睛看的话)而且我们还需要 void
类型的变量,可能来自 void
的转换至 object
, 等等。基本上,
void
对于所有意图和目的,都必须成为常规类型(例如 a regular empty struct )。这的含义并不可怕,但我认为您可以理解为什么 C# 和 Java 避免了它。What about the second option - postpone overload resolution?
也完全有可能,但请注意,它会有效地将泛型转换为较弱的模板。 (“较弱”的意思是 C++ templates aren't restricted to typenames 。)
同样,这不会是世界末日,但它会失去我之前描述的泛型的优势。 C# 和 Java 的设计者显然希望保持这些优势。
边注:
在 C# 中,我知道有一种特殊情况,即在验证泛型类型定义之后进行绑定(bind)。如果您有
new()
对 T
的约束而你试图instantiate a new T()
,编译器将生成检查是否 T
的代码是不是值类型。然后:new T()
变成 default(T)
-- 记得C# default struct constructors aren't really constructors in the CLR sense . Activator.CreateInstance
被调用,这是使用反射的间接构造函数调用。 这种特殊情况非常特殊,因为即使它完全推迟了到运行时的方法绑定(bind),编译器仍然可以执行一次静态分析、类型检查和代码生成。毕竟表达式的类型
new T()
总是 T
并且可以轻松解决和验证对具有空形式参数列表的事物的调用。
关于java - 为什么 C++ 中的 void 方法可以返回空值,而在其他语言中却不能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36404736/