Martin's Tex-Blog

Posts on programming and generally technical topics

Platform dependend overload resolution ambiguity

leave a comment »


Here I will show case when types used internally by compiler might cause ambiguities in overload resolution. Below is example code:

#include <string>
struct CoolString {
    std::string s;
    CoolString(const char* p) : s(p){}
    char& operator[](size_t i) { return s.at(i); }
   operator char*() { return &s[0]; }
};

int main()  {
    CoolString ss("asd");
    ss[1] = '1';  // {1}
}
  

It looks valid, althought use of implicit user conversion operator to char* is a sign of bad design (std::string provides c_str instead). Above code compiles under g++ and clang, but fails to compile under Visual Studio. The error is that call {1} is ambiguous, and looks like below:

2>c:\prj\test.cpp(10) : error C2666: 'CoolString::operator []' : 2 overloads have similar conversions
2>        c:\prj\test.cpp(10): could be 'char &CoolString::operator [](size_t)'
2>        or 'built-in C++ operator[(char *, int)'
2>        while trying to match the argument list '(CoolString, int)'

so, compiler is unable to decide which one of those to choose:

ss.operator[](1) // line 1
ss.operator char*()[1] // line 2

In case of second choice ( line 2 ), compiler wants to apply built in subscript operator after user defined conversion to char*. Built in subscript operator takes index as a ptrdiff_t type. Because this is a typedef, lets check how it differs under g++/clang from VS:

std::cout << "size_t = " << typeid(size_t).name() << std::endl; // VS2013: unsigned int, g++: unsigned long
std::cout << "ptrdiff_t = " << typeid(ptrdiff_t).name() << std::endl; // VS2013: int, g++: long

Under VS ptrdiff_t is int, while under g++/clang its long. While size_t is always unsigned. Lets look what conversions are required by both candidates:

ss.operator[](1) – here only integral promotion of int to unsigned int or unsigned long is required

ss.operator char*()[1] – here on VS only user defined conversion must be applied to ss (so that operator char*() is called), since 1 is an int it matches ptrdiff_t which under VS is also int. But under g++/clang two conversions are requried: first user defined to char*, and then promotion of int to long.

Now its clear why VS shows error, implicit conversions sequences are ambiguous – both are of same lenghts, also they differ on different positions, but I am not clear about it here (exact standard wording) – you could argue that integral promotion should be more promoted than user defined conversion.

Under g++/clang built in operator is not choosen because it requires two implicit conversions, first is user defined conversion, second is promotion int to long. So compiler choses ss.operator[](1) which requires only integral promotion.

The solution is to:
1. Dont use implicit conversion operator to char*
2. Replace size_t with int in char& operator[](size_t i), this will make it a perfect match and top priority candidate.
3. Use ss[1u]

For more references:
http://stackoverflow.com/questions/1726740/c-error-operator-2-overloads-have-similar-conversions
– “C++ Templates – The Complete Guide” – B.2.1

Advertisements

Written by Marcin

January 17, 2015 at 3:10 am

Posted in C++, Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: