【C++11】显式转换操作符

隐式类型转换是C++的一个既好又坏的特性。它给人以方便,但可能造成一些十分隐晦的错误。
类型转换提供了一个类型向另一个类型的构造。
  1.   

  1. class X  
  2. {  
  3. public:  
  4.     operator int() const noexcept  
  5.     {  
  6.         return 42;  
  7.     }  
  8. };  
  9.   
  10. void Func(int) {}  
  11.   
  12. int wmain()  
  13. {  
  14.     X x0;  
  15.     X x1;  
  16.     Func(x0);  
  17.     Func(x1);  
  18.   
  19.     int n = x0 + x1;  
  20.     std::cout << n << std::endl; // 84  
  21.   
  22.     return 0;  
  23. }  

上面的代码中,X可以隐式地转换为int,于是函数Func可以接受X类型的参数,x0与x1也可以用+来做运算。

在实际的编程工作中,一个更常见的例子是,我们自己定义的字符串类(记为String)重载了operator const wchar_t*():

  1.   
  1. class String  
  2. {  
  3. public:  
  4.     operator const wchar_t*() const noexcept  
  5.     {  
  6.         // 函数体  
  7.     }  
  8. };  

从而,如果一个函数需要const wchar_t*类型的参数,就可以直接传入一个String实例。


但是,重载类型转换也不是万无一失的。比如,重载operator bool()。

重载operator bool()所带来的问题比较多,以至于很多企业的编码规范中,都不提倡甚至禁止重载operator bool()。

由于bool属于算数类型,所以重载了operator bool()的类的实例可以被用在任何需要算术类型的上下文中。

  1.   
  1. class Y  
  2. {  
  3. private:  
  4.     int m_;  
  5. public:  
  6.     explicit Y(int m) :m_{ m } {}  
  7.     operator bool() const noexcept  
  8.     {  
  9.         return (m_ != 0);  
  10.     }  
  11. };  
  12.   
  13. int wmain()  
  14. {  
  15.     Y y0{ 12 };  
  16.     Y y1{ 25 };  
  17.     auto n = y0 + y1; // !!!  
  18.     std::cout << n << std::endl;  
  19.     return 0;  
  20. }  

毫无意义的y0 + y1竟然(无警告地)编译通过,而且还通过+产生了一个int,这实在不合理。可能程序作者想要的是Y(38),更可能的是后来维护代码的人根本无法知道原作者想干什么。随着代码的规模变大,这些细微的隐患会越埋越深,或许,将来花费两天时间找到的BUG就是由它引起的。


为了防止这样的异常情况,C++11引入了显式的类型转换运算符

  1.   
  1. class X  
  2. {  
  3. public:  
  4.     explicit operator int() const noexcept  
  5.     {  
  6.         return 42;  
  7.     }  
  8. };  
  9.   
  10. void Func(int) {}  
  11.   
  12. int wmain()  
  13. {  
  14.     X x0;  
  15.     Func(x0); // 错误,不存在从 X 到 int 的(隐式)转换  
  16.     int y = x0; // 错误,不存在从 X 到 int 的(隐式)转换  
  17.     Func((int)x0); // 正确1  
  18.     Func(int(x0)); // 正确2  
  19.     Func(static_cast<int>(x0)); // 正确3  
  20.   
  21.     return 0;  
  22. }  

用explicit修饰的类型转换运算符,则相应的类型转换必须显式地进行。C式(正确1),函数(正确2),static_cast(正确3)都行。


但是,显式的类型转换有一个例外。如果表达式被用作条件,那么显式的operator bool()也可以隐式地进行(仅限转换到bool)。“被用作条件”即:

  • if、while及do语句的条件部分;

  • for语句头的条件表达式;

  • 逻辑非运算符(!)、逻辑或运算符(||)、逻辑与运算符(&&)的运算对象;

  • 条件运算符(x ? y : z)的条件表达式。

由于转换到bool一般被用作条件,所以operator bool()一般用explicit来修饰。

  1.   
  1. class K  
  2. {  
  3. public:  
  4.     explicit operator bool() const noexcept  
  5.     {  
  6.         return false;  
  7.     }  
  8. };  
  9.   
  10. int wmain()  
  11. {  
  12.     K k0;  
  13.     if (k0) // 正确  
  14.     {  
  15.         std::cout << "qwer" << std::endl;  
  16.     }  
  17.     else  
  18.     {  
  19.         std::cout << "zxcv" << std::endl;  
  20.     }  
  21.     return 0;  
  22. }  
阅读更多
换一批

没有更多推荐了,返回首页