这个程序在C ++中编译和运行,但是没有多种语言,比如Java和C#。
#include <iostream>
using namespace std;
void foo2() {
cout << "foo 2.\n";
}
void foo() {
return foo2();
}
int main() {
foo();
return 0;
}
在Java中,这给出了编译器错误,例如&#39; Void方法不能返回值&#39;。但由于被调用的方法本身就是一个空白,因此它不会返回一个值。我知道为了便于阅读,可能会禁止这样的结构。还有其他异议吗?
编辑:为了将来参考,我在这里发现了一些类似的问题return-void-type-in-c-and-c 以我的拙见,这个问题还没有得到解答。 回复&#39;因为它在规范中这样说,所以继续&#39;并没有削减它,因为有人必须首先编写规范。也许我应该问过&#39;允许返回像C ++这样的无效类型有什么优缺点?
答案 0 :(得分:20)
这是因为它可能在模板中使用。 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#中有两组通用通用委托,即Func<>
和Action<>
?好吧,Action<T>
正好因为Func<T, void>
被禁止而存在。 C ++设计人员并不想在可能的情况下引入这样的情况,所以他们决定允许你使用void
作为模板参数 - 你找到的案例就是一个促进这一点的功能。 / p>
(请允许我以假装Q&amp; A格式写下其余部分。)
但为什么C#和Java 不允许类似的构造?
首先,要了解如何使用这些语言实现泛型编程:
为什么选择一种实现通用编程的方法呢?
好的,那么C#和Java需要做些什么来支持
void
作为有效的泛型参数呢?
我不得不推测回答这个问题,但我会尝试。
在语言层面,他们必须放弃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中,重载解析发生在&#34;早期&#34;而不是&#34;迟到&#34;。对于每个可能的TResult
,重载决策算法将选择相同的被调用者。类型检查器将 进行投诉,因为您要么从非void
方法返回void
表达式,要么返回来自可能void
方法的非void
表达式。
换句话说,外部方法可以是通用的,除非:
如果我们使用第一个选项怎么办 - 让被调用者的返回类型变为通用并继续前进?
我们可以这样做,但它只是将我们的问题推送给被叫方。
在某些时候,我们需要一些方法来实例化&#34;某种void
实例,并且可选择以某种方式接收它。所以现在我们需要void
的构造函数(尽管每个void
方法可以算作工厂方法,如果你斜视)我们还需要void
类型的变量,来自{的可能转换{1}}至void
,依此类推。
基本上,object
必须成为所有意图和目的的常规类型(例如a regular empty struct)。这种影响并不可怕,但我认为你可以看出为什么C#和Java会避免它。
第二个选项怎么样 - 推迟重载决议?
也完全有可能,但请注意,它会有效地将泛型转换为较弱的模板。 (&#34;较弱&#34;在C++ templates aren't restricted to typenames的意义上。)
同样,它不会成为世界末日,但它会失去我之前描述的泛型的优势。 C#和Java的设计者显然希望保持这些优势。
旁注:
在C#中,我知道有一个特殊情况,在验证通用类型定义之后发生绑定。如果void
上有new()
约束,并且您尝试instantiate a new T()
,编译器将生成检查T
是否为值类型的代码。然后:
T
变为new T()
- 请记住C# default struct constructors aren't really constructors in the CLR sense。default(T)
,这是使用反射的间接构造函数调用。这个特殊情况非常特殊,因为即使它已经完全推迟了将方法绑定到运行时,编译器仍然可以执行静态分析,类型检查和代码生成一次。毕竟,表达式Activator.CreateInstance
的类型始终是new T()
,并且可以轻松地解析和验证对具有空形式参数列表的事物的调用。
答案 1 :(得分:4)
根据Java Language Specification §14.17:
不包含Expression的return语句必须包含在下列之一中,否则会发生编译时错误:
- 使用关键字void声明的方法,不返回值(第8.4.5节)
...
带有Expression的return语句必须包含在下列之一中,否则会发生编译时错误:
- 声明为返回值的方法
...
因此,通过声明方法为void
,您说它不返回任何值,因此您只能使用不带表达式的return;
语句。