类型转换运算符

class SmallInt {
public: SmallInt(int i = 0) : val(i) {
if (i < 0 || i > 255)
throw std::out_of_range("Bad SmallInt value");
}
operator int() const { return val; }
private:
size_t val;
};

在这个类中,类型转换运算符为

operator int() const { return val; }

类型转换的形式为:
operator Type() const;

Type表示除了void之外任意类型,只要该类型能作为函数的返回类型。因此,不允许转换成数组或函数。但是可以转换为指针或引用类型。

类型转换函数既没有显示的返回类型, 也没有形参,必须定义为成员函数。

编译器值能执行一次进一步转换,但是我们将SmallInt转换为int后,可以将int转换为任何其他算术类型。

尽管类型转换函数不负责指定返回类型,但实际上每个类型转换函数都会返回一个对应类型的值:
class SmallInt {
public:
int operator int() const; // 错误, 指定了返回类型
operator int(int = 0) const; // 错误, 参数列表不为空
operator int*() const { return 42; } // 错误,42不是一个指针
}

显示的类型转换运算符


在C++11里,加入了显示的类型转换运算符:
class SmallInt {
public: SmallInt(int i = 0) : val(i) {
if (i < 0 || i > 255)
throw std::out_of_range("Bad SmallInt value");
}
explicit operator int() const { return val; }
private:
size_t val;
};

添加explicit后,编译器不会将一个显示的类型转换用于隐式类型转换:
SmallInt si = 3; // 构造函数不是explicit的
si + 3; // 错误, 类型转换是explicit的
static_cast<int>(si) + 3; // 正确,显示的请求类型转换

但是存在一个例外,当表达式被用作条件, 编译器才会将显示的类型转换用于它,即就是,当表达式出现在以下的位置时,显示的类型转换被隐式执行:
if、while及do语句的条件部分;
for语句头的条件表达式;
逻辑与或非运算符的运算对象
条件运算符( ? : )的条件表达式。


在C++11中,IO标准库定义了一个向bool类型的显示类型转换,因此我们可以使用IO库类型对象来判断一个流是否正确:
while (std::cin >> value) ;

当cin读完值后,由于在while的条件部分,因此显示的类型转换被隐式的执行,cin转换为一个bool类型,用于判断是否读取成功。



避免有二义性的类型转换

struct A {
A() = default;
A(const B&);
}; struct B {
operator A() const;
}; A f(const A&);
B b;
A a = f(b); // 二义性错误

上面的代码中,发生了二义性错误,调用f函数时,既可以使用以B为参数的A的构造函数,也可以使用B当中把B转换为A的类型转换运算符。效果相当,因此出现二义性。


可以显示调用函数名来执行对应的调用:
A a1 = f(b.operator A()); // 调用B的类型转换运算符
A a2 = f(A(b)); // 调用A的构造函数

尽量不要定义多重的内置类型转换,除了显示的向bool类型转换之外,应该尽量避免定义类型转换函数。

重载函数与转换构造函数

当重载函数的参数分别属于不同的类类型时,如果这些类恰好同时定义了同样的转换构造函数,那么二义性问题进一步提升:

struct C {
C(int);
}; struct D {
D(int);
}; void manip(const C&);
void manip(const D&);
manip(10); // 二义性错误,到底是manip(C(10))还是manip(D(10))呢

可以显示的调用:

manip(C(10));

重载函数与用户自定义的类型转换

struct C {
C(int);
};
struct E {
E(double);
}; void manip2(const C&);
void manip2(const E&); manip2(10); // 二义性错误,是manip2(C(10)) 还是 manip2(E(double(10)));

因为调用重载函数所请求的用户定义的类型转换不止一个且彼此不同,所以具有二义性。即使其中一个需要额外的标准类型转换而另一个调用能精确匹配,编译器也会将该调用标示为错误。


函数匹配与重载运算符


class SmallInt {
friend SmallInt operator + (const SmallInt&, const SmallInt&);
public:
SmallInt(int = 0);
operator int() const { return val; }
private:
size_t val;
}; SmallInt s1, s2;
SmallInt s3 = s1 + s2; // 使用重载的operator +
int i = s3 + 0; // 二义性错误

将s1和s2相加赋值给s3, 执行了重载的加法运算符。而最后一句有二义性错误,0可以转换成SmallInt,然后使用SmallInt的+,或者把s3转换为int,对两个int使用内置版本的+。

如果我们对同一个类既提供了转换目标是算数类型的类型转换,也提供了重载的运算符,则会遇到重载运算符与内置运算符的二义性问题。



C++ Primer : : 第十四章 : 重载运算符与类型转换之类型转换运算符和重载匹配的更多相关文章

  1. C++ Primer : 第十四章 : 重载运算与类型转换之重载运算符

    重载前须知 重载运算符是特殊的函数,它们的名字由operator和其后要重载的运算符号共同组成. 因为重载运算符时函数, 因此它包含返回值.参数列表和函数体. 对于重载运算符是成员函数时, 它的第一个 ...

  2. C++Primer 第十四章

    //1.当运算符作用于类类型运算对象时,可以通过运算符重载重新定义该运算符的含义.明智的使用运算符重载能令程序更加易于编写和阅读. //2.重载的运算符是具有特殊名字的函数,它们由关键字operato ...

  3. 【C++】《C++ Primer 》第十四章

    第十四章 重载运算与类型转换 一.基本概念 重载运算符是具有特殊名字的函数:由关键字operator和其后要定义的运算符号共同组成.也包含返回类型.参数列表以及函数体. 当一个重载的运算符是成员函数时 ...

  4. C++ Primer Plus学习:第十四章

    第十四章 C++中的代码重用 包含对象成员的类 将类的对象作为新类的成员.称为has-a关系.使用公有继承的时候,类可以继承接口,可能还有实现(纯虚函数不提供实现,只提供接口).使用包含时,可以获得实 ...

  5. C Primer Plus_第四章_字符串和格式化输入输出_编程练习

    Practice 1.输入名字和姓氏,以"名字,姓氏"的格式输出打印. #include int main(void) { char name[20]; char family[2 ...

  6. 《Linux命令行与shell脚本编程大全》 第十四章 学习笔记

    第十四章:呈现数据 理解输入与输出 标准文件描述符 文件描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误 1.STDIN 代表标准输入.对于终端界面 ...

  7. perl 第十四章 Perl5的包和模块

    第十四章 Perl5的包和模块 by flamephoenix 一.require函数  1.require函数和子程序库  2.用require指定Perl版本二.包  1.包的定义  2.在包间切 ...

  8. Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  9. C和指针 (pointers on C)——第十四章:预处理器

    第十四章 预处理器 我跳过了先进的指针主题的章节. 太多的技巧,太学科不适合今天的我.但我真的读,读懂.假设谁读了私下能够交流一下.有的小技巧还是非常有意思. 预处理器这一章的内容.大家肯定都用过.什 ...

随机推荐

  1. Java 中的 static 使用之静态方法

    与静态变量一样,我们也可以使用 static 修饰方法,称为静态方法或类方法.其实之前我们一直写的 main 方法就是静态方法.静态方法的使用如: 运行结果: 需要注意: 1. 静态方法中可以直接调用 ...

  2. Java注释@Override

    @Override指定方法覆载.它可以强制一个子类必须覆盖父类的方法. package ch14; /** * Created by Jiqing on 2016/12/27. */ public c ...

  3. Error: Cannot find a valid baseurl for repo: base

    解决方法如下(修改dns配置) vi /etc/resolv.conf 在此文件最后加入:nameserver 8.8.8.8 如果没有vi编辑器可用: echo "nameserver 8 ...

  4. Canvas与Image互相转换

    转换 Image为 Canvas要把图片转换为Canvas(画板,画布),可以使用canvas元素 context 的drawImage方法: // 把image 转换为 canvas对象 funct ...

  5. 笨办法学 Python (Learn Python The Hard Way)

    最近在看:笨办法学 Python (Learn Python The Hard Way) Contents: 译者前言 前言:笨办法更简单 习题 0: 准备工作 习题 1: 第一个程序 习题 2: 注 ...

  6. python的基础类源码解析——collection类

    1.计数器(counter) Counter是对字典类型的补充,用于追踪值的出现次数. ps:具备字典的所有功能 + 自己的功能 ################################### ...

  7. C#微信公众号开发之网页授权oauth2.0获取用户基本信息(一)

    咨询 请加 QQ::QQ群: 在微信里面,非认证的公众号账号,只能通过在微信回复菜单单击等事件获取openid,但是认证的公众账号(之前认证的订阅号是不可以的,现在新开放了政府媒体机构的认证订阅号)可 ...

  8. iOS开发Swift篇—(三)字符串和数据类型

    iOS开发Swift篇—(三)字符串和数据类型 一.字符串 字符串是String类型的数据,用双引号""包住文字内容  let website = "http://www ...

  9. PhoneGap与Jquery Mobile组合开发android应用的配置

    PhoneGap与Jquery Mobile结合开发android应用的配置 由于工作需要,用到phonegap与jquery moblie搭配,开发android应用程序. 这些技术自己之前也都没接 ...

  10. jQuery键盘控制方法,以及键值(keycode)对照表

    键盘控制应用范围非常广泛,比如快捷键控制页面的滚动:在填写表单时候,限制输入内容:或者是屏蔽复制.粘贴.退后等功能.这里说说用jQuery比原生态的JS好用,代码简单清晰,不要问我JS怎么写,因为我不 ...