按返回类型重载函数?

2023-12-11

为什么更主流的静态类型语言不支持按返回类型重载函数/方法?我想不出有什么可以做到的。它看起来并不比支持参数类型重载有用或合理。怎么人气这么低?


与其他人所说的相反,按返回类型重载is可能和is由一些现代语言完成。通常的反对意见是在类似的代码中

int func();
string func();
int main() { func(); }

你不知道哪个func()正在被呼叫。这可以通过以下几种方式解决:

  1. 有一个可预测的方法来确定在这种情况下调用哪个函数。
  2. 每当出现这种情况时,就会出现编译时错误。然而,有一个允许程序员消除歧义的语法,例如int main() { (string)func(); }.
  3. 不要有副作用。如果您没有副作用并且从不使用函数的返回值,那么编译器可以避免首先调用该函数。

我经常使用的两种语言(ab)按返回类型使用重载:Perl and Haskell。让我描述一下他们的工作。

In Perl,之间有一个根本的区别scalar and list上下文(以及其他,但我们假设有两个)。 Perl 中的每个内置函数都可以根据不同的情况执行不同的操作context它被称为。例如,join运算符强制列表上下文(在正在连接的事物上),而scalar运算符强制标量上下文,因此比较:

print join " ", localtime(); # printed "58 11 2 14 0 109 3 13 0" for me right now
print scalar localtime(); # printed "Wed Jan 14 02:12:44 2009" for me right now.

Perl 中的每个运算符在标量上下文中执行某些操作,在列表上下文中执行某些操作,并且它们可能不同,如图所示。 (这不仅仅适用于像localtime。如果你使用数组@a在列表上下文中,它返回数组,而在标量上下文中,它返回元素的数量。例如print @a打印出元素,同时print 0+@a打印尺寸。)此外,每个操作员都可以force上下文,例如添加+强制标量上下文。中的每个条目man perlfunc记录了这一点。例如,这是条目的一部分glob EXPR:

在列表上下文中,返回一个(可能是 空)文件名扩展列表 的价值EXPR比如标准 Unix外壳/bin/csh会做。在 标量上下文,glob 迭代 这样的文件名扩展,返回 当列表耗尽时 undef。

现在,列表和标量上下文之间的关系是什么?出色地,man perlfunc says

请记住以下重要规则: 没有相关的规则 列表中表达式的行为 其标量行为的上下文 上下文,反之亦然。它可能会做 两个完全不同的事情。每个 运算符和函数决定哪个 这是最有价值的 适合以标量返回 语境。一些运算符返回 列表的长度 在列表上下文中返回。一些 运算符返回第一个值 列表。一些运算符返回 列表中的最后一个值。一些 运算符返回成功​​的计数 运营。一般来说,他们做什么 你想要的,除非你想要一致性。

所以这不是一个简单的问题,只有一个函数,然后在最后进行简单的转换。事实上,我选择的是localtime出于这个原因的例子。

不仅仅是内置程序具有这种行为。任何用户都可以使用定义这样的函数wantarray,它允许您区分列表、标量和 void 上下文。因此,例如,如果您在 void 上下文中被调用,您可以决定不执行任何操作。

现在,你可能会抱怨这不是true按返回值重载,因为您只有一个函数,该函数被告知调用它的上下文,然后对该信息进行操作。然而,这显然是等价的(类似于 Perl 不允许通常的字面重载,但函数只能检查其参数)。而且,它很好地解决了本回复开头提到的模棱两可的情况。 Perl 不会抱怨它不知道调用哪个方法;而是抱怨它不知道调用哪个方法。它只是调用它。它所要做的就是找出函数被调用的上下文,这总是可能的:

sub func {
    if( not defined wantarray ) {
        print "void\n";
    } elsif( wantarray ) {
        print "list\n";
    } else {
        print "scalar\n";
    }
}

func(); # prints "void"
() = func(); # prints "list"
0+func(); # prints "scalar"

(注意:当我指的是函数时,有时我可能会说 Perl 运算符。这对于本次讨论并不重要。)

Haskell采用另一种方法,即没有副作用。它还具有强大的类型系统,因此您可以编写如下代码:

main = do n <- readLn
          print (sqrt n) -- note that this is aligned below the n, if you care to run this

此代码从标准输入读取浮点数,并打印其平方根。但这有什么令人惊讶的呢?嗯,类型readLn is readLn :: Read a => IO a。这意味着对于任何可以Read(正式来说,每个类型都是Read类型类),readLn可以阅读。 Haskell 如何知道我想读取浮点数?嗯,类型sqrt is sqrt :: Floating a => a -> a,这本质上意味着sqrt只能接受浮点数作为输入,因此 Haskell 推断出了我想要的。

当 Haskell 无法推断我想要什么时会发生什么?嗯,有几种可能性。如果我根本不使用返回值,Haskell 一开始就不会调用该函数。然而,如果我do使用返回值,那么 Haskell 会抱怨它无法推断类型:

main = do n <- readLn
          print n
-- this program results in a compile-time error "Unresolved top-level overloading"

我可以通过指定我想要的类型来解决歧义:

main = do n <- readLn
          print (n::Int)
-- this compiles (and does what I want)

不管怎样,整个讨论的意思是,通过返回值进行重载是可能的并且已经完成,这回答了您的部分问题。

你问题的另一部分是为什么更多的语言不这样做。我会让其他人来回答这个问题。然而,有几点评论:主要原因可能是这里产生混淆的机会确实比按参数类型重载更大。您还可以查看各个语言的基本原理:

Ada:“看起来最简单的重载解析规则是使用所有内容 - 来自尽可能广泛的上下文的所有信息 - 来解析重载引用。这个规则可能很简单,但没有帮助。它要求人类读者扫描任意大的文本片段,并做出任意复杂的推论(例如上面的(g))。我们认为更好的规则是明确人类读者或编译器必须执行的任务,并且使该任务为对人类读者来说尽可能自然。”

C++(Bjarne Stroustrup 的“C++ 编程语言”第 7.4.1 小节):“在重载解析中不考虑返回类型。原因是保持单个运算符或函数调用的解析与上下文无关。请考虑:

float sqrt(float);
double sqrt(double);

void f(double da, float fla)
{
    float fl = sqrt(da);     // call sqrt(double)
    double d = sqrt(da); // call sqrt(double)
    fl = sqrt(fla);            // call sqrt(float)
    d = sqrt(fla);             // call sqrt(float)
}

如果考虑返回类型,将不再可能查看对sqrt()隔离并确定调用了哪个函数。”(请注意,为了比较,在 Haskell 中没有implicit转换。)

Java (Java 语言规范 9.4.1): “其中一个继承的方法必须是每个其他继承方法的返回类型可替换的,否则会发生编译时错误。” (是的,我知道这并没有给出一个基本原理。我确信这个基本原理是 Gosling 在《Java 编程语言》中给出的。也许有人有一份副本?我敢打赌这本质上是“最小惊喜原则”。 )然而,关于 Java 的一个有趣事实:JVMallows按返回值重载!例如,这用于Scala,并且可以访问直接通过Java以及通过玩弄内部结构。

附言。最后一点,实际上可以通过 C++ 中的返回值通过技巧进行重载。证人:

struct func {
    operator string() { return "1";}
    operator int() { return 2; }
};

int main( ) {
    int x    = func(); // calls int version
    string y = func(); // calls string version
    double d = func(); // calls int version
    cout << func() << endl; // calls int version
    func(); // calls neither
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

按返回类型重载函数? 的相关文章

随机推荐

  • Three.js:使用变形目标获取更新的顶点

    我有一些变形目标正在工作 https jsfiddle net 3wtwzuh3 2 使用滑块控件查看变形 但是 我希望能够访问变形后顶点的新位置 如果您在链接的示例中注意到 我正在显示立方体第一个顶点的 y 坐标 但它没有更新 This
  • ORA-01422: PL/SQL 精确获取返回的行数多于请求的行数

    我不断收到此错误 我无法弄清楚出了什么问题 DECLARE 第 1 行错误 ORA 01422 精确提取返回的行数多于请求的行数ORA 06512 在第 11 行 这是我的代码 DECLARE rec ENAME EMPLOYEE ENAM
  • 以编程方式控制折线图中的符号等

    我有一个 JavaFX 应用程序 我希望用户能够控制折线图中的符号 线条样式等 我不想创建多个样式表 而是想将此功能构建到 Java 代码中 感谢 Jewelsea 之前发布的优秀示例 我可以动态更改线条样式 这太棒了 但我无法更改默认符号
  • 此正则表达式 (RegEx) 标志有何含义 /iD

    我想使用这个正则表达式来验证 IPv6 但我想了解它所做的一切https stackoverflow com a 1934546 3112803 gt gt a f0 9 1 4 gt 1 7 a f0 9 gt 8 1 gt 1 0 6
  • .NET 如何从电子邮件中提取嵌入的图像?

    您好 我正在开发 NET 1 1 中的一个项目 我需要从我收到的电子邮件中提取 并将其保存在某处 嵌入图像 有人可以告诉我从哪里开始吗 谢谢 从 POP 服务器下载的电子邮件将是文本格式 您必须解析整个电子邮件 并找到所有 img 标签具有
  • SharePoint Online 中的 Office 365 Workbooks.open 打开空白文件

    我们公司正在使用 Office 365 Pro Plus 的半年更新频道 最近我们从版本 1708 更新到了 1803 我们正在使用一些自动化宏 而不是从 SharePoint 在线租户打开文件 自更新以来 调用了 Workbooks op
  • 将 pandas 数据帧写入 xlsm 文件(启用宏的 Excel)

    写一个pandas DataFrame到 Excel 工作簿中 xlsx格式很简单 import pandas as pd df pd DataFrame firstColumn 5 2 0 10 4 secondColumn 9 8 21
  • AntiXss.HtmlEncode 与 AntiXss.GetSafeHtmlFragment

    谁能告诉我这两者之间的区别吗 AntiXss HtmlEncode vs AntiXss GetSafeHtmlFragment Html编码实际上对标签进行编码 AntiXss HtmlEncode b hello b
  • Java MongoDB:com.mongodb.DB 和 com.mongodb.client 之间有什么区别

    我是 MongoDB 的新手 我正在一个 Java 项目中工作 我开始了一些教程来开始使用驱动程序 我正在使用com mongodb client直到我注意到里面没有findOne方法com mongodb client MongoColl
  • 示例 MQTT 客户端代码不工作 C

    我从那里得到了示例代码here 我有头文件MQTTClient h以及 但是 当我构建时 我收到错误 undefined reference to MQTTClient create undefined reference to MQTTC
  • 如何使用 Blazor 生成并保存文件客户端?

    我想要一个 SPA 来完成客户端的所有工作 甚至生成一些图表 视觉效果 我希望能够让用户单击按钮并保存页面中的视觉效果 表格和其他内容 既可见又不可见 因此右键单击保存或复制 粘贴并不总是一个选项 如何从 web assembly blaz
  • 如何通过管道传输字符串来处理'STDIN?

    我有一个命令需要来自管道的输入 例如 考虑一下著名的cat命令 echo Hello cat Hello 假设我在 Perl 6 程序中有一个字符串 我想将其通过管道传输到命令 use v6 my input Hello This is t
  • flutter - 如何在应用程序启动和使用时下载资产

    目前 我的应用程序有许多资产 图像 声音 字体 json SQL lite 数据库文件等 全部已定义在pubspec yaml 但是 由于要求减少 APK 大小 我需要其中一些应用程序启动时下载并将其保存到存储中 下次无需下载 如果资源尚未
  • 如何使用 Spring MVC 从 url 下载文件?

    我的 jsp 中有这样的下载选项 a href img src images download gif alt Download border 0 align right 上面的 url 是文件的位置 名称是文件名 单击jsp中的下载选项时
  • awk每列n条数据的平均值

    使用 awk 将值存储在数字列表中 提供使用 awk 对列中每组 3 个点进行平均的解决方案 如何将其扩展到保持格式的无限数量的列 例如 2457135 564106 13 249116 13 140903 0 003615 0 00344
  • 检测并修复 JavaScript 中的循环引用

    鉴于我在一个大型 JavaScript 对象中有一个循环引用 我尝试JSON stringify problematicObject 浏览器抛出 类型错误 将循环结构转换为 JSON 这是预期的 那我想找到这个循环引用的原因 最好使用Chr
  • git 警告“无法访问权限被拒绝”在 .gitignore 目录上

    我的一个名为 Database 的目录中有一个名为 gitignore 的子目录 我希望 git 忽略它 所以我将其列在我的 gitingore 文件中 gitignore Bb in Dd ebug Rr elease gitignore
  • 在视口组件上使用 JScrollPane 鼠标侦听器

    我有一个JScrollPane可以设置其视口View到一系列不同的面板 我想在单击其视口中的任何其他组件时获取 JScrollPane 组件 如果我添加一个MouseListener对于 JScrollPane 当我直接单击窗格边框时 它会
  • 在 VBA 中下载文件并存储

    我需要下载从 REST 搜索中获得的文件 网址如下 由于隐私原因 我需要对其进行编辑 该文件应该是 Nastran 计算的结果 可以通过简单的文本编辑器查看 扩展名是 pch 比较大 21mb 如何在 VBA 中实现呢 首先 链接不起作用
  • 按返回类型重载函数?

    为什么更主流的静态类型语言不支持按返回类型重载函数 方法 我想不出有什么可以做到的 它看起来并不比支持参数类型重载有用或合理 怎么人气这么低 与其他人所说的相反 按返回类型重载is可能和is由一些现代语言完成 通常的反对意见是在类似的代码中