C++ 运算符重载(二) | 类型转换运算符,二义性问题
文章目录
- 类型转换运算符
- 概念
- 避免过度使用类型转换函数
- 解决上述问题的方法
- 转换为 bool
- 显式的类型转换运算符
- 类型转换二义性
- 重载函数与类型转换结合导致的二义性
- 重载运算符与类型转换结合导致的二义性
类型转换运算符
概念
类型转换运算符(conversion operator)是类的一种特殊成员函数。负责将一个类类型的值转换成其他类型。
operator type() const ;
其中 type
表示某种类型。类型转换运算符可以面向任意类型(除了 void
之外)进行定义,只要该类型能作为函数的返回类型。因此,我们不允许转换成数组或者函数类型,但允许转换成指针(包括数组指针及函数指针)或者引用类型。
一个类型转换函数必须是类的成员函数;它不能声明返回类型,形参列表也必须为空。类型转换函数通常不应该改变待转换对象的内容,因此,应该是const。
运用实例,定义一个简单的类,令其表示 0~255
之间的一个整数:
构造函数将算术类型的值转换成 SmallInt
对象,而类型转换运算符将 SmallInt
对象转换成 int
:
SmallInt si;
si = 4; // 将 4 隐式转换成 SmallInt,然后调用 SmallInt::operator=
si + 3; // 首先将 si 隐式地转换成 int,然后执行整数的加法
尽管编译器一次只能执行一个 我们定义的类型转换(如上面的构造函数/类型转换运算符),但可以将其搭配 内置类型转换(如double可以转换成int) 实现二次转换。
// 内置类型转换将 doulbe 实参转换成 int
SmallInt si = 3.14; // 调用 SmallInt(int) 构造函数,然后调用拷贝构造函数
// SmallInt 的类型转换运算符将 si 转换成 int
si + 3.14; // 内置类型将所得的 int 继续转换成 double
尽管类型转换函数不负责指定返回类型,但实际上每个类型转换函数都会返回一个对应类型的值:
避免过度使用类型转换函数
- 类型转换可能具有误导性
例如,假设某个类表示 Date
,我们也许会为它添加一个从 Date
到 int
的转换。然而,类型转换函数的返回值应该是什么?
- 一种可能的解释是,函数返回一个十进制数,依次表示年、月、日,例如,
July 30,1989
可能转换为int
值19890730
。 - 同时还存在另外一种合理的解释,即类型转换运算符返回的
int
表示的是从某个时间节点(比如January 1,1970
)开始经过的天数。
问题在于 Date
类型的对象和 int
类型的值之间不存在明确的一对一映射关系。因此在此例中,不定义该类型转换运算符也许会更好。作为替代的手段,类可以定义一个或多个普通的成员函数以从各种不同形式中提取所需的信息。
- 类型转换运算符可能产生意外结果
对于类来说,定义向 bool
的类型转换还是比较普遍的现象。
int i = 42;
cin << i; // 如果向 bool 的类型转换不是显式的,则该代码在编译器看来是合法的
因为 istream
本身并没有定义 <<
,所以本来代码应该产生错误。然而,该代码能使用 istream
的 bool类型转换运算符
将 cin
转换成 bool
,而这个 bool值
接着会被提升成 int
并用作内置的左移运算符的左侧运算对象。这样一来,提升后的 bool值(1或0)
最终会 被左移42个位置。 这一结果显然与我们的预期大相径庭。
解决上述问题的方法
转换为 bool
- 标准库的早期版本中,
IO
类型定义了向void*
的转换规则,以求避免上述问题。 - 在
C++11
标准中,IO
标准库通过定义一个向bool
的显式类型转换实现同样的目的。
其实我们在编程中经常用到 IO
类型定义的 operator bool
:
while(std::cin >> value)
为了对条件求值,cin
被 istream operator bool
类型转换函数隐式地执行了转换。如果 cin
的条件状态是 good
,则该函数返回为真;否则该函数返回为假。(这部分知识可以看我之前的博客)
向 bool
的类型转换通常用在条件部分,因此 operator bool
一般定义成 explicit
的。
显式的类型转换运算符
为了防止上面第二点这样的异常情况发生,我们可以使用 explicit
关键字。
SmallInt si = 3; // 正确:SmallInt 的构造函数不是显式的
si + 3; // 错误:explicit阻止隐式类型转换
static_cast<int>(si) + 3; // 正确:显式地请求类型转换
当类型转换运算符是显式的时,我们也能执行类型转换,不过必须通过显式的强制类型转换才可以。
该规定存在一个例外,即,如果表达式被用作条件,则编译器会将显式的类型转换自动应用于它。 换句话说,当表达式出现在下列位置时,显式的类型转换将被隐式地执行:
if
、while
及do
语句的条件部分for
语句头的条件表达式- 逻辑非运算符(
!
)、逻辑或运算符(||
)、逻辑与运算符(&&
)的运算对象 - 条件运算符(
? :
)的条件表达式。
类型转换二义性
如果类中包含一个或多个类型转换,则必须确保在类类型和目标类型之间只存在唯一一种转换方式。否则的话,我们编写的代码将很可能会具有二义性。
在两种情况下可能产生多重转换路径:
- 第一种情况是 两个类提供相同的类型转换: 例如,当
A类
定义了一个接受B类
对象的转换构造函数,同时B类
定义了一个转换目标是A类
的类型转换运算符。 - 第二种情况是 类定义了多个转换规则,而某些转换规则可以通过其他类型转换实现。 这种情况多出现在算术运算符上。
通常情况下,不要为类定义相同的类型转换,也不要在类中定义两个及两个以上转换源或转换目标是算术类型的转换。
第一种情况举例:
解决方法是显式调用:
A a1 = f(b.operator A());
A a2 = f(A(b));
第二种情况举例:
我们使用两个用户定义的类型转换时,如果转换函数之前或之后存在标准类型转换,则标准类型转换将决定最佳匹配到底是哪个:
short s = 42;
// 把 short 提升成 int 优于 提升成 double
// 上面的 long 则没有int和double谁优于谁的规则,因此会有二义性
A a3(s); // A::A(int)
重载函数与类型转换结合导致的二义性
有时会出现这种情况:
或这种情况:
虽然我们可以通过显式地构造正确的类型而消除二义性:
manip(C(10)); // 调用 manip(const C&)
manip2(E(double(10))); // 调用 manip2(const E&)
但意味着程序的设计存在不足。
重载运算符与类型转换结合导致的二义性
重载的运算符也是重载的函数。因此也遵从通用的函数匹配规则。例如,如果 a
是一种类类型,则表达式 a sym b
可能是:
a.operatorsym(b); // a 有一个 operatorsym 成员函数
operatorsym(a, b); // operatorsym 是一个普通函数
和普通函数不同,我们无法通过调用的形式区分当前调用的是成员函数还是非成员函数。
举个例子:
- 第一条加法语句接受两个
SmallInt
值并执行+
运算符的重载版本。 - 第二条加法语句具有二义性:因为我们可以把
0
转换成SmallInt
,然后使用SmallInt
的+
;或者把s3
转换成int
,然后对于两个int
执行内置的加法运算。
如果我们对同一个类既提供了转换目标是算术类型的类型转换,也提供了重载的运算符,则将会遇到重载运算符与内置运算符的二义性问题。
C++ 运算符重载(二) | 类型转换运算符,二义性问题相关推荐
- C++ 重载强制类型转换运算符
C++ 中,类型的名字(包括类的名字)本身也是一种运算符,即类型强制转换运算符. 类型强制转换运算符是单目运算符,也可以被重载,但只能重载为成员函数,不能重载为全局函数.经过适当重载后,(类型名)对象 ...
- C++ 运算符重载与类型转换
1. 当一个重载的运算符是成员函数时,this绑定到左侧运算对象.成员运算符函数的(显式)参数数量比运算对象的数量少一个. 通常情况下,不应该重载逗号.取地址.逻辑与和逻辑或运算符. 关于重载运算符的 ...
- C++类与对象笔记十二:运算符重载二:左移运算符重载
左移运算符重载:可以打印输出自定义数据类型. 为了输出重载,我们先看看现有的输出函数.输出类型为std下的ostream类型的引用. 标准输出流(全局只能有一个). 返回值类型为ostream,函数名 ...
- 运算符重载之友元运算符重载
友元可以参考:https://blog.csdn.net/aaqian1/article/details/84427884 友元运算符重载函数: 把运算符重载函数定义为某个类的友元函数. 1.定义友元 ...
- 运算符重载之成员运算符重载
成员运算符重载函数 成员运算符重载函数,即把运算符重载函数定义为某个类的成员函数. 1.定义成员运算符重载函数的语法形式 (1)在类的内部,定义成员运算符重载函数的格式如下: 函数类型 operato ...
- C++ 运算符重载和类型转换函数
C++中对于用户自定义类对象的运算,要进行重载操作.如 class overload{public:... private:int num; }; overload o1, o2; int num; ...
- python 运算符重载_Python3面向对象-运算符重载
1:运算符重载介绍 运算符重载,就是在某个类的方法中,拦截其内置的操作(比如:+,-,*,/,比较,属性访问,等等),使其实例的行为接近内置类型. 当类的实例出现在内置操作中时(比如:两个实例相加 + ...
- C++_类和对象_C++运算符重载_函数调用运算符重载_---C++语言工作笔记060
然后我们再来看函数调用运算符重载, 其实这个,重载后的,函数调用运算符有点像,仿函数,什么是仿函数,一会我们再说 我们去写一个MyPrint类 里面重载函数调用运算符,可以看到函数调用运算符的重载的写 ...
- C++_类和对象_C++运算符重载_加号运算符重载_实现两个对象相加_对象和int类型相加_通过成员函数重载+号_全局函数重载+号_以及重载_运算符重载函数实现---C++语言工作笔记055
然后我们再来看,运算符的重载,首先我们来看加号的运算符的重载. 这个的作用是很明显的,比如我们有两个Person对象,p1,p2,如果我们想 p3 = p1+p2能可以嘛,不可以对吧,因为系统给我们提 ...
- c 运算符重载前置++_C ++运算符重载–综合指南
c 运算符重载前置++ Hello, folks! In this article, we will understand a very interesting yet magical power p ...
最新文章
- 手机号码格式验证和 FASTDFS 工具类
- 基于ubuntu 的LAMP 优化加固
- 运行自己的shell脚本
- yum安装php和apache先装哪个,yum如何安装apache与php
- python 下载公众号文章_python3下载公众号历史文章
- CAFFE怎样跑起来
- 6-4 二叉树的非递归遍历 (25分)_本周小结!(二叉树)
- var lib mysql ib_MYSQL问题解决
- SpringBoot : Consider defining a bean of type xxx in your configuration.
- 活与死、回调以及面向对象设计
- 在lamp环境下搭建多种论坛(下)
- 【TSP】基于matlab蚁群算法求解31城市旅行商问题【含Matlab源码 1147期】
- 在线教学生计算机,洪恩老兔轻松教你学电脑
- pdg转pdf与djvu转pdf大法
- [渝粤教育] 中国地质大学 建筑艺术赏析 复习题
- 手游传奇架设教程_传奇手游战神引擎架设教程
- HIF转16位TIF或者PNG
- 江西师范大学教育心理测量计算机,江西师范大学重点和特色专业介绍
- [转载] 七龙珠第一部——第111话 龟仙人最后的魔封波
- java的round函数怎么用_Java Math round()用法及代码示例
热门文章
- python解释器的提示符是shell嘛_python解释器怎么运行
- 图像 pipeline_多面体优化,Pipeline与深度学习编译器
- 计算机动画 应用,计算机动画与应用.PDF
- a5d2 phy驱动
- 定制mini2440 wince启动界面
- php中拼接html代码,如何利用ajax给html动态拼接代码
- matlab的灰色关联,五种灰色关联度分析matlab代码
- 【转】c#中@的3种作用
- Sharepoint页面里添加.net托管代码
- 第五节: Quartz.Net五大构件之Trigger的四大触发类