我已经看到关于此的其他问题,但没有一个完全解释它。编译器处理以下两种情况的正确方法是什么?我用gcc 4.7.1(使用-std = c ++ 0x),VS2010和VS2012试了一下,得到了不同的结果:
示例1:
struct BB
{
// generic cast
template<typename T>
operator T() const
{
return 0;
}
// string cast
operator std::string() const
{
return string("hello");
}
};
int main()
{
BB b;
string s = b;
}
输出:
示例2:
struct BB
{
// generic cast
template<typename T>
operator T() const
{
return 0;
}
// string cast
operator std::string() const
{
return string("hello");
}
};
int main()
{
BB b;
string s = (string)b;
输出:
答案 0 :(得分:4)
你的第二个带C风格演员的版本含糊不清。问题是static_cast<std::string>
有两种方法可以将类BB
的实例转换为字符串。显而易见的路径是使用非模板std :: string强制转换运算符直接转换为字符串。有一条更加狡猾的道路,除了VS2010以外都发现了它。另一个路径是使用模板转换操作符将BB
转换为字符串的分配器,然后使用此分配器构造一个空字符串。
模板转换操作符是潜在危险的野兽。您刚刚为编译器提供了将BB
转换为任何内容的工具。
您的第一个版本可以解决此问题,因为std::basic_string(const _Alloc& __a)
是一个显式构造函数。显式构造函数可以由显式转换使用,但不能由隐式转换使用。正是这个构造函数加上转换为创建歧义的分配器,并且此路径不能与隐式转换一起使用。
至于为什么VS1012在隐含转换上失败,它可能是VS2012中的一个错误,或者可能是C ++ 11创建了更多从BB
到std::string
的途径。我不是C ++ 11专家。我甚至不是新手。
还有一件事:如果你使用了
,你的代码会失败得更厉害 std::string s;
s = b;
赋值运算符与模板转换运算符一起创建了更多从b
到s
的方法。系统应该将b
转换为std::string
,char
还是char*
?
结论:您真的应该重新考虑使用模板转换运算符。
答案 1 :(得分:2)
在一些编译器下失败的原因是因为他们试图找出你在做什么的不同长度。罪魁祸首是模板化的操作员调用。
正如this SO question所解释的那样,必须明确调用模板化运算符(b.operator()<std::string>
如果要调用template<typename T> operator();
),但是,无法调用模板化运算符:
b.operator std::string()<std::string>; //invalid
b.operator std::string <std::string>(); //also invalid, string takes no template arguments
// a bunch more weird syntax constructions...
问题来自于operator
之后的名称取决于模板参数,因此无法指定它。 gcc和VS2012弄清楚你在做什么,并注意到他们可以适应转换到模板或明确定义的一个=&gt;暧昧的电话。 VS2010没有这样做并且正在调用其中一个,你可以通过调试找到哪一个。
模板专业化可能在这样的情况下有所帮助,但是,尝试定义
// string cast
template<>
operator std::string() const
{
return string("hello");
}
将失败,因为编译器无法区分该函数与没有template<>
的常规函数之间的区别。如果原型中有一些参数可以使用,但operator typename
确实有...
长话短说 - 避免模板转换操作符。