Troels
Troels

Reputation: 177

Clang typecasting using C++11 has ambiguous compiler behaviour

Consider the following code:

#include<iostream>
#include<vector>
using namespace std;
class Foo {
public:
  template< typename T> 
  operator vector< T >() const {
    return vector< T >();
  }

  template< typename T> 
  operator T() const {
    return T();
  }
};

int main () {
  Foo b;
  vector< int >  q = b;
  q = b;
}

Compiling this with Clang or g++ using either of the two commands:

g++ test.cpp
clang++ test.cpp

Enabling C++11 features, however, it fails:

g++ --std=c++0x test.cpp
clang++ --std=c++11 test.cpp

The error message reads as follows:

test.cpp:20:5: error: use of overloaded operator '=' is ambiguous (with operand types 'vector<int>' and 'Foo')
  q = b;
  ~ ^ ~
/usr/include/c++/4.6/bits/stl_vector.h:373:7: note: candidate function
      operator=(vector&& __x)
      ^
/usr/include/c++/4.6/bits/stl_vector.h:362:7: note: candidate function
      operator=(const vector& __x);
      ^
/usr/include/c++/4.6/bits/stl_vector.h:394:7: note: candidate function
      operator=(initializer_list<value_type> __l)
      ^
1 error generated.

It is unclear to me why it works without C++11, while it fails with. Moveover, note that the line

vector< int >  q = b; // In the main function, line 19

in the main function does not cause an error. Can anyone explain why it does not work, and what one can do to make it work with C++11?

Upvotes: 1

Views: 1277

Answers (1)

Ben Voigt
Ben Voigt

Reputation: 283634

There's no compiler bug here. Your code is broken in C++11, because C++11 added more converting constructors and more overloads for the assignment operator.

This is the risk you run when you make a type that converts to absolutely anything (using templated conversion). Foo is just as happy to convert itself to an initializer_list<int> as a vector<int>.

The reason that

vector<int> q = b;

works in Clang 3.1, while

vector<int> q(b);

fails, is that the first is copy-initialization, which requires an implicit conversion to vector<int> followed by a copy-constructor call, while the second is direct-initialization which performs an explicit conversion. The set of candidates for implicit conversion is smaller, because constructors marked explicit are removed, resolving the ambiguity.

The difference between Clang 3.0 and 3.1 is likely a library compliance fix, which marked additional constructors as explicit, not a change to compiler behavior.

Upvotes: 2

Related Questions