c++castingoperator-overloadingtypecasting-operator

How do I avoid cast operator() and access operator[] conflict?


I was certain it was asked before, but I couldn't find it.

I have a class encapsulating std::string. Inside I want to overloaded both typecasts to char* and the access operator[].

class String
{
public :
    String(const char* s) { m_str = s; }
    const char* str() const  { return m_str.c_str(); }
    char* str()  {  return &m_str[0]; }
    char operator[](size_t pos) const { return m_str[pos]; }
    char& operator[](size_t pos) { return m_str[pos]; }
    operator const char*() const { return str(); }  // cast operator
    operator char*() { return str(); }  // cast operator
protected:
    std::string m_str;
};

void main()
{
    String s = "1234";
    if(s[0] != '1')   //'String::operator []': 4 overloads have similar conversions !!!!!
        std::cout << "Error" << endl;
}

Problem is now of course when I try to use String [] I receive error:

4 overloads have similar conversions

The compiler can't decide what to do:

  1. use the overload operator[] directly
  2. first convert to char*, use operator(), and finally operator[] over the resulting char* array

Is there some way to keep both operators? For many functions, it is very helpful to have char* and const char* to use operator() of String but in all other cases directly the operator[] (not trying to convert to char* first).

I suppose the answer to this is probably No. That is why in std::string there is no char* cast operator, but maybe something has changed in more recent C++ standards?

EDIT: I can compile the above with GCC but it fails with VS (x86).

Error C2666 'String::operator []': 4 overloads have similar conversions


Solution

  • Always compile your code with warnings enabled, for gcc and clang the command line option is -Wall -Wextra -Werror. The warnings will tell you that operator[] needs to return a value.


    In expression s[0] in 32-bit mode clang and msvc compilers consider the standard conversion of int index to unsigned (32-bit size_t) to be of the same rank as user-defined conversion of String to char*. See the compiler error messages here. The latter conversions being a user-defined conversion has worse rank than any standard conversion, but the compilers cannot resolve this tie. I am not sure which exactly standard rule applies here.

    A fix is to add another overload of operator[] that accepts a signed ptrdiff_t index:

    char operator[](ptrdiff_t pos) const { return m_str[pos]; }
    char& operator[](ptrdiff_t pos) { return m_str[pos]; }