简体   繁体   English

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

[英]conversion operator overloading ambiguity, compilers differ

I have seen other questions on SO regarding this, but none that explains it in full. 我已经看到了关于此的其他问题,但没有一个完全解释它。 What is the right ways for compilers to handle the two situations below? 编译器处理以下两种情况的正确方法是什么? I have tried it with gcc 4.7.1 (with -std=c++0x), VS2010 and VS2012 an get different results on all: 我用gcc 4.7.1(使用-std = c ++ 0x),VS2010和VS2012尝试了一下,得到了不同的结果:

Example 1: 例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;
}

Output: 输出:

  • gcc 4.7.1: Ok gcc 4.7.1:好的
  • VS2010: Ok VS2010:好的
  • VS2012: Fail: "Cannot convert from BB to string" VS2012:失败:“无法从BB转换为字符串”

Example 2: 例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;

Output: 输出:

  • gcc 4.7.1: Fail: call of overloaded string(BB&) is ambigious gcc 4.7.1:失败:调用重载字符串(BB&)是不明智的
  • VS2010: Ok VS2010:好的
  • VS2012: Fail: "Cannot convert from BB to string" VS2012:失败:“无法从BB转换为字符串”

Your second version with a C-style cast is ambiguous. 你的第二个版本与C风格的演员是不明确的。 The problem is that there are two ways that static_cast<std::string> can convert an instance of class BB to a string. 问题是static_cast<std::string>有两种方法可以将BB类的实例转换为字符串。 The obvious path is by using your non-template std::string cast operator to convert directly to a string. 显而易见的路径是使用非模板std :: string强制转换运算符直接转换为字符串。 There is a more devious path, and all but VS2010 have found it. 有一条更加狡猾的道路,除了VS2010以外都发现了它。 This other path is to use your template cast operator to convert a BB to an allocator for a string and then construct an empty string using this allocator. 另一个路径是使用模板转换运算符将BB转换为字符串的分配器,然后使用此分配器构造空字符串。

Template cast operators are potentially dangerous beasts. 模板转换操作符是潜在危险的野兽。 You have just given the compiler the tools to convert a BB to anything . 您刚刚为编译器提供了将BB转换为任何内容的工具。

Your first version escapes this problem because std::basic_string(const _Alloc& __a) is an explicit constructor. 你的第一个版本逃避了这个问题,因为std::basic_string(const _Alloc& __a)是一个显式的构造函数。 Explicit constructors can be used by explicit conversions but not by implicit ones. 显式构造函数可以由显式转换使用,但不能由隐式转换使用。 It is this constructor plus the conversion to an allocator that creates the ambiguity, and this path cannot be used with an implicit conversion. 正是这个构造函数加上转换为创建歧义的分配器,并且此路径不能与隐式转换一起使用。

As to why VS1012 fails on the implied conversion, it could be a bug in VS2012, or it could be that C++11 creates even more avenues to get from a BB to a std::string . 至于为什么VS1012在隐含转换上失败,它可能是VS2012中的一个错误,或者可能是C ++ 11创建了从BBstd::string更多途径。 I am not a C++11 expert. 我不是C ++ 11专家。 I'm not even a novice. 我甚至不是新手。

One more item: your code would have failed even more miserably had you used 还有一件事:如果你使用过,你的代码会失败得更厉害

 std::string s;
 s = b;

The assignment operator in conjunction with the template conversion operator creates more ways to get from b to s . 赋值运算符与模板转换运算符一起创建了更多从bs Should the system convert b to a std::string , to a char , or a char* ? 系统应该将b转换为std::stringchar还是char*

Bottom line: You really should rethink your use of template conversion operators. 结论:您真的应该重新考虑使用模板转换运算符。

The reason is fails under some compilers is because they go to different lengths trying to figure out what you're doing. 在一些编译器下失败的原因是因为他们试图找出你在做什么的不同长度。 The culprit is the templated operator call. 罪魁祸首是模板化的操作员调用。

As this SO question explains, templated operators have to be called explicitly ( b.operator()<std::string> if you wanted to call template<typename T> operator(); ), but, there is no way to call your templated operator: 正如这个SO问题所解释的那样,必须显式调用模板化运算符( 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...

The issue comes from the fact that the name after operator depends on the template argument, so there is no way to specify it. 问题来自于operator后面的名称取决于模板参数,因此无法指定它。 gcc and VS2012 figured out what you were doing and noticed they can fit the conversion to either the template or the explicitly defined one => ambiguous call. gcc和VS2012弄清楚你在做什么,并注意到它们可以适应转换到模板或明确定义的一个=>模糊的调用。 VS2010 didn't do so and is calling one of them, you can find which one through debugging. VS2010没有这样做并正在调用其中一个,你可以通过调试找到哪一个。

Template specialization could've helped in a case like this, however, trying to define 模板专业化可能在这样的情况下有所帮助,但是,试图定义

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

will fail, since the compiler can't tell the difference between that function and a regular one without the template<> . 将失败,因为编译器无法区分该函数与没有template<>的常规函数​​之间的区别。 If there were some arguments in the prototype it would've worked, but operator typename does have any... 如果原型中有一些参数它会工作,但operator typename确实有...

Long story short - avoid templated conversion operators. 长话短说 - 避免模板转换运算符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM