C++对重装的参数匹配规则疑问
代码如下:
// file : tt.cpp
#include <string>
#include <iostream>
using namespace std;
typedef unsigned char u8;
typedef unsigned int u32;
// 这个类重装了到std::string, unsigned char, unsigned int三个类型的类型转换方法(代码的返回值只是演示)
class CC
{
public:
operator string() const
{
return "string ";
}
operator u8() const
{
return 8;
}
operator u32() const
{
return 32;
}
};
// 这个类的“=”符号重装了接受三个不同的类型
class TT
{
public:
TT& operator = (const string& str)
{
cout < < "using string " < < endl;
return *this;
}
TT& operator = (signed char cc)
{
cout < < "using char " < < endl;
return *this;
}
TT& operator = (const char* ptr)
{
cout < < "using pointer " < < endl;
return *this;
}
};
// 编译命令:g++ tt.cpp -o tt
int main(int argc, char* argv[])
{
TT tt;
CC cc;
// 下面这个的原意是希望cc转换成std::string,然后通过“=”操作符号赋值给tt的。
tt = cc;
// 但是,在GCC3.4上编译总是报告“混淆”,错误如下
//tt.cpp: In function `int main(int, char**) ':
//tt.cpp:49: error: ambiguous overload for 'operator= ' in 'ttt = cc '
//tt.cpp:29: note: candidates are: TT& TT::operator=(const std::string&)
//tt.cpp:34: note: TT& TT::operator=(signed char)
//tt.cpp:39: note: TT& TT::operator=(const char*) <near match>
// 在VC2005和GCC3.2上都能正确的匹配,符合原意。
// 我的怀疑是不是GCC3.4的类型匹配规则有BUG?按照最匹配原则,应该匹配std::string,而u8要是想匹配到TT& operator = (signed char cc)必须有一个精度损失的类型转换才能完成,显然不应该是一个合适的参数匹配,因此,不应该存在参数匹配混淆才对。
return 0;
}
// 请教各位大侠,是不是有什么GCC的参数没设置正确导致?又或者是我的理解不对?还是GCC的BUG?望高手指点。
// BTW:不要告诉我用tt = (string) cc;这样的方式,这个方式肯定是可以找到正确的参数匹配的,但是现有代码很多,改起来工作量很大,而且也不优雅:)
[解决办法]
太多的自定义类型转换符的出现,一般都是一个错误。
建议你改掉这个风格。
[解决办法]
通信实体之间已经约定好了0x1001就是传递字符串,0x1002就是传递u8整数
------------------------------
这种情况在很多地方出现,最典型的就是数据库访问。这里的核心问题是动态的类型转换。使用类型操作符是最危险的方案。可靠和准确永远是首要的,只有在前者的基础上才能谈论优雅。所以,std::string val1 = getTLV(0x1001).getString(); 是更好的选择。就像std::string那样。凡是涉及到类型转换,显式的永远比隐式的更加安全可靠。
当然也有一些优化方案可供选择。一种方案是使用函数模板,及其特化:
template <int tag>
void getTLV();
template <>
string getTLV <0x1001> (){...}
template <>
u8 getTLV <0x1002> (){...}
...
如此,使用时,通过实参实例化模板,获得正确的函数签名:
std::string val1 = getTLV <0x1001> ();
u8 val2 = getTLV <0x1002> ();
std::string val1 = getTLV <0x1002> (); //编译错误,返回类型不匹配
u8 val2 = getTLV <0x1001> (); //同上
这样,无需采用类型转换操作符,即可解决类型匹配问题。而不同类型间的转换,如u8=> string等可以通过重载operator=或者专用的转换函数实现。重载operator=的副作用大大小于类型转换操作符。