8

我在 SO 上看到了其他关于此的问题,但没有一个可以完整地解释它。编译器处理以下两种情况的正确方法是什么?我已经使用 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&)的调用不明确
  • VS2010:好的
  • VS2012:失败:“无法从 BB 转换为字符串”
4

2 回答 2

4

您使用 C 风格演员表的第二个版本是模棱两可的。问题是有两种方法static_cast<std::string>可以将类的实例转换为BB字符串。显而易见的路径是使用非模板 std::string 强制转换运算符直接转换为字符串。还有一条更曲折的路,除了VS2010,其他人都找到了。另一种方法是使用模板转换运算符将 a 转换BB为字符串的分配器,然后使用此分配器构造一个空字符串。

模板转换运算符是潜在的危险野兽。您刚刚为编译器提供了将 a 转换BB任何东西的工具。

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

至于为什么 VS1012 在隐含转换上失败,可能是 VS2012 中的一个错误,也可能是 C++11 创造了更多从 aBB到 a 的途径std::string。我不是 C++11 专家。我什至不是新手。

还有一项:如果你使用过,你的代码会失败得更惨

 std::string s;
 s = b;

赋值运算符与模板转换运算符一起创建了更多从bto获取的方法s。系统应该转换b为 a std::string、 achar还是 a char*

底线:你真的应该重新考虑你对模板转换运算符的使用。

于 2012-10-15T09:53:16.933 回答
2

在某些编译器下失败的原因是因为它们会竭尽全力试图弄清楚你在做什么。罪魁祸首是模板化的操作员调用。

正如这个 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 弄清楚了你在做什么,并注意到它们可以适应模板或明确定义的一个 => 模糊调用的转换。VS2010没有这样做,正在调用其中一个,你可以通过调试找到哪一个。

在这种情况下,模板专业化可能会有所帮助,但是,尝试定义

// string cast
template<>
operator std::string() const
{ 
    return string("hello");
}

将失败,因为编译器无法区分该函数和没有template<>. 如果原型中有一些参数它会起作用,但operator typename确实有任何......

长话短说 - 避免使用模板化转换运算符。

于 2012-10-15T10:00:39.890 回答