标量类型和运算符重载的成员函数

Member functions for scalar types and operator overloading

本文关键字:成员 函数 重载 运算符 类型 标量      更新时间:2023-10-16

我一直在思考C++可能具有的一些功能,有人知道为什么不支持它们吗?

  1. 内置类型的成员函数这似乎没有必要,但却是一个有趣的功能。示例伪代码:int int::getFirstBit(void) {return *this & 1;} ... int a = 2; a.getFirstBit();这可能看起来没用,但实现起来也不难。随之而来的是以下想法:
  2. 类定义之外的成员函数除了与访问限制(public、protected、private等(和封装冲突之外,我不明白为什么不支持这一功能,但也许只有structs才能具有此功能
  3. 非对象类型的运算符重载,可以用于指针或数组

我知道这些功能不需要太多,但它们看起来仍然很酷。是因为它们看起来没有必要,还是因为它们会引起很多头痛?

我知道这些功能不需要太多,但它们看起来仍然很酷。是因为它们看起来没有必要,还是因为它们会引起很多头痛?

一部分的一部分的另一部分。添加到语言中的每一个新功能都会增加语言、编译器和程序的复杂性。一般来说,除非有真正的激励需求(或者新功能将有助于编写更简单、更安全的程序(,否则不会添加功能。

关于您建议的特定功能:

1-内置类型的成员功能

没有必要,您可以对具有免费函数的成员函数执行任何您想执行的操作,所需成本相同,并且用户代码中唯一的区别是函数的参数位于.之前或括号内。

自由函数唯一不能做的就是动态调度(多态性(,但由于不能从这些类型派生,所以也不能有多态性。再说一遍,要做到这一点,你需要2个。

2-类定义之外的成员函数。

我理解你指的是C#中的扩展方法,在C#中,可以从外部将新方法添加到类型中。很少有这种功能的使用在没有它的情况下不够简单。然后就是复杂性。

目前,编译器看到类的单个定义,并且能够确定可以应用于该类型元素的所有成员方法。这包括virtual函数,这意味着编译器可以立即确定虚拟函数表(虽然vtable不是标准,但所有实现都使用它们(。如果您可以在类定义之外添加虚拟方法,那么不同的转换单元将看到不同的类型的不兼容视图。对第三个虚拟函数的调度可能是在一个.cpp文件中调用foo,但在另一个文件中调用bar。在不推迟将二进制文件加载到内存中执行的链接阶段的大部分时间的情况下解决这个问题几乎是不可能的,而推迟它将意味着语言模型的重大变化。

如果您将该功能限制为非虚拟函数,事情会变得更简单,因为调用将直接分派到函数,但尽管如此,即使这样也意味着其他级别的复杂性。使用C++中的单独编译模型,您最终将不得不为原始类和扩展方法编写头,并将它们都包含在您想要使用它的翻译单元中,在大多数情况下,你可以通过在原始头中声明那些相同的方法作为真正的成员方法来简化它(或者自由函数,自由函数确实是用户定义类型接口的一部分!(

此外,允许这样做意味着代码中简单的拼写错误可能会产生意想不到的结果。目前,编译器会验证成员函数的定义是否有正确的声明,如果允许,则必须删除该检查,并且在声明或定义中写入名称时的简单拼写错误将导致两个单独的函数,而不是快速修复编译器错误。

3-非对象类型的操作员过载

该语言允许重载所有用户定义类型的运算符,包括类和枚举。对于其余的类型,有一组运算符已经用精确的语义定义,不能更改。同样,对于单独的编译模型,这意味着1+2在不同的翻译单元中可能意味着不同的东西,特别是includes的确切组合可能会改变程序的语义,这将造成严重破坏——您删除了标头中的依赖项,并删除了包含const char* + int重载的include,这反过来意味着包含头部的代码中"Hi" + 2的语义发生了变化,从用户定义的操作到产生指向字符串的nul终止符的指针。这真的很危险,因为这意味着程序的一个部分的简单更改可能会导致程序的其他部分不正确。

即使对于没有当前意义的组合(char* + int*(,也可以使用常规函数来提供相同的操作。请记住,只有当在建模操作的域中,该操作自然被理解为具有特定语义时,才应该重载该运算符,这就是为什么可以重载用户定义的类型,但指针不是域的一部分,而是语言的一部分。在该语言中,"Hi" + new int(5)的含义没有自然定义。运算符重载的目的是使代码更可读,在任何没有自然定义的上下文中,运算符重载都会产生完全相反的效果。

  1. 因为你可以编写一个做同样事情的自由函数。"成员函数"是否更可取,以抵消在标准中批准它并让编译器供应商实现它的巨大成本?没有
  2. 外面到底在哪里
  3. 参见1