转换运算符重载歧义,编译器不同

时间:2012-10-15 09:28:52

标签: c++ visual-studio gcc operators

我已经看到关于此的其他问题,但没有一个完全解释它。编译器处理以下两种情况的正确方法是什么?我用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;
}

输出:

  • gcc 4.7.1:好的
  • VS2010:好的
  • VS2012:失败:“无法从BB转换为 字符串“

示例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;

输出:

  • gcc 4.7.1:失败:调用重载字符串(BB&amp;)是暧昧的
  • VS2010:好的
  • VS2012:失败:“无法从BB转换为字符串”

2 个答案:

答案 0 :(得分:4)

你的第二个带C风格演员的版本含糊不清。问题是static_cast<std::string>有两种方法可以将类BB的实例转换为字符串。显而易见的路径是使用非模板std :: string强制转换运算符直接转换为字符串。有一条更加狡猾的道路,除了VS2010以外都发现了它。另一个路径是使用模板转换操作符将BB转换为字符串的分配器,然后使用此分配器构造一个空字符串。

模板转换操作符是潜在危险的野兽。您刚刚为编译器提供了将BB转换为任何内容的工具。

您的第一个版本可以解决此问题,因为std::basic_string(const _Alloc& __a)是一个显式构造函数。显式构造函数可以由显式转换使用,但不能由隐式转换使用。正是这个构造函数加上转换为创建歧义的分配器,并且此路径不能与隐式转换一起使用。

至于为什么VS1012在隐含转换上失败,它可能是VS2012中的一个错误,或者可能是C ++ 11创建了更多从BBstd::string的途径。我不是C ++ 11专家。我甚至不是新手。

还有一件事:如果你使用了

,你的代码会失败得更厉害
 std::string s;
 s = b;

赋值运算符与模板转换运算符一起创建了更多从bs的方法。系统应该将b转换为std::stringchar还是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确实有...

长话短说 - 避免模板转换操作符。