该程序可以用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#中如何有两组通用的通用委托,即Func<>
和Action<>
?好吧,Action<T>
之所以存在是因为Func<T, void>
被禁止。 C ++设计人员不想在任何可能的情况下引入这种情况,因此他们决定允许您将void
用作模板参数,而您发现的情况正是为此提供便利的功能。
(让我以假装的问答格式来写其余的内容。)
但是,为什么C#和Java不允许类似的构造?
首先,了解如何使用这些语言进行通用编程:
C#和Java泛型通过解析泛型类型(或方法)定义并确保它对您提供的泛型约束/界限有效来工作。
C ++模板a search-and-replace mechanism周围具有强大的元编程语言。 They are not required to make sense in the absence of specific template arguments -仅当他们掌握实际参数时,它们才从“模板元语言”转换为“ C ++语言”(可以这么说)。
为什么选择一种实现通用编程的方法而不是另一种?
泛型方法保留了其余语言的nominal typing。这样做的好处是允许(AOT)编译器执行一次静态分析,类型检查,错误报告,重载解析并最终生成代码。
模板方法本质上是duck typing。鸭子使用名义上的类型的语言打字没有上述优点,但是从某种意义上讲,它允许您更大的灵活性,因为它将允许潜在的“无效”事物(从名义上类型系统的角度来看是“无效”),例如只要您实际上在程序中的任何地方都没有提到那些无效的可能性。换句话说,模板使您可以统一表达更多的案例。
好的,那么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中,重载解析发生在“早期”,而不是“后期”。重载解析算法将为每个可能的TResult
选择相同的被叫方。而且类型检查器将不得不抱怨,因为您要么从可能不是void
的方法返回void
表达式,要么是从可能是void
的方法返回非void
表达式。
换句话说,外部方法不能通用,除非:
被调用方也是通用的,其返回类型由与外部方法匹配的通用类型参数定义。
泛型类型和方法中的重载解析会推迟到实际类型参数可用之前,以便我们可以在调用点选择正确的非泛型方法。
如果我们选择第一种方法-将被调用者的返回类型设为通用并继续前进,该怎么办?
我们可以这样做,但这只会将我们的问题推给被呼叫者。
在某个时候,我们将需要某种方式来“实例化”某种void
实例,并有选择地能够以某种方式接收它。因此,现在我们将需要void
的构造函数(尽管您可以斜视每个void
方法都可以算作一种工厂方法),并且还需要类型void
的变量,以及从void
到object
的可能转换。 , 等等。
基本上,出于所有意图和目的,void
必须成为常规类型(例如a regular empty struct)。这样的含义并不可怕,但是我认为您可以理解为什么C#和Java避免使用它。
第二种选择-推迟过载解析呢?
也完全有可能,但是请注意,它将有效地将泛型转换为较弱的模板。 (在C++ templates aren't restricted to typenames的意义上为“较弱”。)
再说一次,这不是世界末日,但这将涉及失去我先前描述的泛型的优点。 C#和Java的设计师显然希望保留这些优势。
边注:
在C#中,我知道一种特殊情况,即在验证通用类型定义之后进行绑定。如果在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
,这是使用反射的间接构造函数调用。
这种特殊情况非常特殊,因为即使已将方法绑定完全推迟到运行时,编译器仍可以一次执行静态分析,类型检查和代码生成。毕竟,表达式new T()
的类型始终为T
,并且可以轻松解析和验证对具有空形式参数列表的对象的调用。
我用eclipse编写了这段代码,用war写过,结果为3d。public static void main(String[] args) { double a = 5d + + + + + +-+3d; System.out.println(a); } 参考方案 您的表情可以改写为(5d) + (+ + + + +-+3d) 其中第一个+是应用于两个操作数的…
Java值加变量++ - java考虑以下代码int val1 = 3; val1++; int val2 = val1++; System.out.println(val1); System.out.println(val2); Val1值= 5;Val2值= 4;为什么Val1的值是“ 5”?据我了解,应该为4,因为:在第1行,它的赋值为3,在第2行,通过val1 ++加上1,结果val…
如何在JNIWrapper中将C++ Array <float,size>转换为jfloatArray? - java我想将我的C ++数组输出映射到jniFloatArray。尝试遵循以下解决方案:“ Convert float* to jfloatArray using JNI”但我无法将float *指向数组对象。假设我在C ++数组输出对象中的输出是:输出= {1.0f,2.0f,3.0f};我真正想要的是将输出(数组)转换或映射到JniWrapper中的jfloa…
在Python和C++之间传输数据而无需写入Windows和Unix文件 - python我有预先存在的python和C ++文件,其中python文件定义了许多点,而C ++代码利用其现有库进行了所需的计算。最终产品是C ++代码写入的文件。我正在寻找一种在python中获取2000点列表的方法,将其传递给函数,然后执行所有C ++代码并输出我需要的文件。其他注意事项。这必须是可以在Linux或Windows机器上工作的东西,并且最少安装新插件…
JAVA:json + websocket - java我正在与朋友一起编程项目。我们将其分为两部分,我负责客户端(简单的窗口应用程序),他制作了服务器。我应该借助websocket将JSON对象发送到他的服务器(他给了我信息,我应该发送http://pastebin.com/dmYBtN25的信息)。我知道如何创建json对象,但是我的问题是如何将websocket lib与json结合使用(当前我正在使用we…