该程序在C++中编译和运行,但不是Java和C#等多种语言.
#include <iostream>
using namespace std;
void foo2() {
cout << "foo 2.\n";
}
void foo() {
return foo2();
}
int main() {
foo();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在Java中,这给出了编译器错误,如'Void方法无法返回值'.但由于被调用的方法本身就是一个void,因此它不返回值.我知道为了便于阅读,可能会禁止这样的结构.还有其他异议吗?
编辑:为了将来的参考,我在这里发现了一些类似的问题return-void-type-in-c-and-c 我个人认为这个问题还没有回答.回复'因为它在规范中这样说,继续'不会削减它,因为有人必须首先编写规范.也许我应该问'允许返回像C++这样的void类型有什么优缺点'?
The*_*kis 20
这是因为它可以在模板中使用.C#和Java禁止void作为类型参数,但C++允许它允许您编写如下模板代码:
template<typename T, typename TResult>
TResult foo(T x, T y)
{
return foo2(x, y);
}
Run Code Online (Sandbox Code Playgroud)
如果void不允许方法返回一个void表达式,如果这个模板实例化是不可能的TResult了void.如果是这种情况,如果您想要TResult实际存在,则需要单独的模板定义void.
例如,请记住在C#中有两组通用通用委托,即Func<>和Action<>?嗯,Action<T>正是因为Func<T, void>被禁止而存在.C++设计人员不希望尽可能地引入这样的情况,因此他们决定允许您将其void用作模板参数 - 您发现的案例就是一个促进这一点的功能.
(请允许我以假装 - 问答形式编写其余内容.)
但为什么C#和Java 没有允许类似的结构?
首先,要了解如何在这些语言中实现泛型编程:
为什么选择一种实现通用编程的方法呢?
好的,那么C#和Java需要做些什么才能支持
void作为有效的泛型参数呢?
我不得不推测回答这个,但我会试试.
在语言层面,他们必须放弃return;仅在void方法中有效并且对于非void方法始终无效的概念.如果没有这种改变,很少有用的方法可以被实例化 - 并且它们都可能必须以递归或无条件结束throw(满足两种方法void和非void方法而不返回).因此,为了使这个有用,C#和Java还必须引入允许您返回void表达式的C++特性.
好的,我们假设您已经拥有了,现在您可以编写如下代码:
void Foo2() { }
void Foo()
{
return Foo2();
}
Run Code Online (Sandbox Code Playgroud)
同样,非通用版本在C#和Java中与在C++中一样无用.但让我们继续前进,看看其实际用途,这是在泛型.
您现在应该可以编写这样的通用代码 - TResult现在可以void(除了已经允许的所有其他类型):
TResult Foo<T, TResult>(T a)
{
return Foo2(a);
}
Run Code Online (Sandbox Code Playgroud)
但要记住,在C#和Java中,重载解析发生在"早期",而不是"迟到".每种可能的重载决策算法都会选择相同的被调用者TResult.并且类型检查器将不得不抱怨,因为您要么void从可能的非void方法返回表达式,要么从可能的方法返回非void表达式void.
换句话说,外部方法不能是通用的,除非:
如果我们使用第一个选项怎么办 - 让被调用者的返回类型变为通用并继续前进?
我们可以做到这一点,但它只是将我们的问题推向被调用者.
在某些时候,我们需要一些方法来"实例化"某种void实例,并且可选地能够以某种方式接收它.所以,现在我们需要构造函数void(尽管每一个void方法可以算作一个工厂方法,如果你斜视),我们还需要类型的变量void,可以转换从void到object,等等.
基本上,void必须成为所有意图和目的的常规类型(例如,常规的空结构).这种含义并不可怕,但我认为你可以看出为什么C#和Java会避免它.
第二个选项怎么样 - 推迟重载决议?
也完全有可能,但请注意,它会有效地将泛型转换为较弱的模板.("较弱"的意思是C++模板不限于类型名称.)
同样,它不会是世界末日,但它将失去我之前描述的泛型的优势.C#和Java的设计者显然希望保留这些优势.
边注:
在C#中,我知道有一个特殊情况,即在验证泛型类型定义之后发生绑定.如果您对a有new()约束T并且您尝试实例化anew T(),则编译器将生成检查是否T为值类型的代码.然后:
new T()变为default(T)- 记住C#默认的struct构造函数不是CLR意义上的构造函数.Activator.CreateInstance调用它,它是使用反射的间接构造函数调用.这种特殊的情况下,是非常特殊的,因为,即使它已经完全推迟方法绑定到运行时,编译器仍然可以进行静态分析,类型检查和代码生成一次.毕竟,表达式的类型new T()总是如此,T并且可以简单地解析和验证对具有空形式参数列表的事物的调用.