Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

Example 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
  • VS2010: Ok
  • VS2012: Fail: "Cannot convert from BB to string"

Example 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
  • VS2010: Ok
  • VS2012: Fail: "Cannot convert from BB to string"
like image 921
Rolle Avatar asked Oct 15 '12 09:10

Rolle


1 Answers

Your second version with a C-style cast is ambiguous. The problem is that there are two ways that static_cast<std::string> can convert an instance of class BB to a string. The obvious path is by using your non-template std::string cast operator to convert directly to a string. There is a more devious path, and all but VS2010 have found it. 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.

Template cast operators are potentially dangerous beasts. You have just given the compiler the tools to convert a BB to anything.

Your first version escapes this problem because std::basic_string(const _Alloc& __a) is an explicit constructor. 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. I am not a C++11 expert. 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. Should the system convert b to a std::string, to a char, or a char*?

Bottom line: You really should rethink your use of template conversion operators.

like image 70
David Hammen Avatar answered Sep 29 '22 03:09

David Hammen