请教:关于操作符重载,隐藏与隐式转换的困惑
有个问题有点疑惑,请教下大家:
且看一下以下这段代码
- C/C++ code
#include <string>class CMyString : public std::string{public: CMyString (); /*explicit */CMyString (const char *pszStr); [0] /*explicit */CMyString (std::string str); [0] ~CMyString(void); CMyString operator += (const CMyString &rStr); CMyString operator + (const CMyString &rStr);};CMyString::CMyString(){}CMyString::CMyString( const char *pszStr ){}CMyString::CMyString( std::string str ){}CMyString::~CMyString( void ){}CMyString CMyString::operator+=( const CMyString &rStr ){}CMyString CMyString::operator+( const CMyString &rStr ){}int main(int argc, char* argv[]){ CMyString myStr; char szTest[] = "test"; myStr += szTest; // [1]ok CMyString* pStr = &myStr; pStr->operator + (szTest); // [2]ok 隐藏? myStr + szTest; // [3] error// error C2666: 'CMyString::operator +' : 2 overloads have similar conversions// could be 'CMyString CMyString::operator +(const CMyString &)'// or 'std::basic_string<_Elem,_Traits,_Ax> std::operator +<char,std::char_traits<char>,std::allocator<_Ty>>(const std::basic_string<_Elem,_Traits,_Ax> &,const char *)' [found using argument-dependent lookup]// with// [// _Elem=char,// _Traits=std::char_traits<char>,// _Ax=std::allocator<char>,// _Ty=char// ]// while trying to match the argument list '(CMyString, char [5])' return 0;}编译器是vs2008,
从提示上看,编译器是弄不清,这里是调用
1. 调用std::string::operator+
还是 2. 先将 const char* 隐式转换成CMyString,然后调用 CMyString operator +
我的疑问是
a. 如果是这样,为什么[1]处,同样的情况,换成operator +=,就不报错,为什么编译器不疑惑调用的是
1. std::string::operator +=
还是 2. CMyString::operator+=
难道说,编译器真正在乎的是这个时候他决定不了+完以后的类型所以才报错?
b. [2]处能正常过编译,是因为隐藏机制在其作用么?
难道说隐藏是只对指针来说的,直接通过对象来调用就没这个说法?
c. 或者说,之所以[1]可以,[3]不行,vs2008的编译器的提示有点误导作用,本质不是决定不了调用哪个函数,而是无法先决定转换方法,因为同样的代码,我用DevC++编译,出来的结果是
ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
网上搜索了下,这个似乎也是发生于类似情况,决定不了使用哪个转换的时候。
d. 既然问了,顺便再夹带一点东西,
http://www.cppblog.com/elprup/archive/2010/05/05/114491.html
这篇文章中的代码
- C/C++ code
#include <iostream>#include <cstdlib>using namespace std;class X{public: X(int xx):x(xx){} int x;};class Y{public: Y(X xx):y(xx.x){} int y;};void f(Y){}int main(){//======case 1====== X x(2); f(x); //ok f(X(2));//ok//======case 2====== f(2); //error:conversion from `int' to non-scalar type `Y' requested system("PAUSE"); return 0;}作者的对这个的解释是,隐式转化只能转换一次,这个是否是有标准依据的?
[解决办法]
看了一下, 解释不出原因, 歧义在于两个构造函数, 为什么=冲突+=不冲突呢, 不知道.
[解决办法]
先将 const char* 隐式转换成CMyString,然后调用 CMyString operator + =
因为你没有重载 CMyString operator + =(const char*)
至于myStr + szTest存在2中路径
1.const char* => CMyString 然后operator +
2.const char* =>string =>CMyString 然后operator+
[解决办法]
个人感觉应该是缺少operator+(char[]),因为+的运算太多了。。。
------解决方案--------------------
我同意6楼的说法
[解决办法]
a. [1] 处 szTest 隐式转化为 CMyString,然后调用 CMyString::operator+=(CMyString&)。
b. [2] 处采用 qualified name lookup,直接找到 CMyString::operator+,std::string::operator+ 压根没进入重载集,因此不形成二义性。
c. [3] 处调用的 + 操作符再用 unqualified name lookup 以及 argument dependent name lookup,后者找到CMyString::operator+ 和 std::string::operator+ 两个候选函数。重载解析无法决议那个调用更合适,因为 CMyString::operator+ 存在 CMyString (const char *pszStr) 的隐式转换,而 std::string::operator+ 存在 CMyString& -> std::string& 的隐式转换。这也是 DevC++ 抱怨的东西,两条路都行,导致二义性,无法决议调用那个函数。[3] 和 [2] 不矛盾,你把 [3] 改成这样,就和 [2] 一样了, myStr.operator + (szTest),这时候一样是 qualified name lookup,一样没有二义性,因为 std::string::operator+ 不可见。
d. f(2) 调用是错误的,不过不是因为隐式转换只能发生一次,而是因为在一个隐式转换序列中,用户自定义转换至多只能出现一次。f(2) 的调用要求 2 -> X(2) -> Y(X(2)),有两次自定义转换。
[解决办法]