ambiguous overload for 'operator[]'

Started by
5 comments, last by Servant of the Lord 12 years, 3 months ago
I'm trying to overload operator[], but am running into a weird compile error when I also overload the typecast operator for int.

Through some googling, I figured out the 'solution' to my problem, but I'm not understanding why there was a problem in the first place, or why this fixes it. I found the answer through a foreign language forum, and google translate didn't do a perfect job in helping me understand what was being said.

If you have a class like this:

class MyClass
{
public:
MyClass(){ }
MyClass &operator [](std::string string) {return *this;}
operator int() {return 0;}
};



And try to compile this code:

MyClass myClass;
myClass["test"];


It gives this compile-time error message:

[color=#aa0000]error: ambiguous overload for 'operator[]' in 'myClass["test"]'
[color=#aa0000]main.cpp:11: note: candidates are: operator[](int, const char*) <built-in>
[color=#aa0000]MyClass.h:5: note: MyClass& MyClass::operator[](std::string)


But works fine if you change the class to this:

class MyClass
{
public:
MyClass(){ }
MyClass &operator [](const char*) {return *this;} //const char* not std::string.
operator int() {return 0;}
};


Why is an operator[](int, const char*) "<built-in>" to my class? What for?
And why, if given the option between:
operator[](int, const char*) and operator[](std::string)
does it decide it's too 'ambiguous', when std::string can be constructed from const char*, and operator[](int, const char*) takes an int as the first parameter?!

The weird thing, which especially confuses me, is why this error only occurs if I'm also overloading the typecast operator int or typecast operator bool, but not if I overload, say, typecast operator float.

I'm guessing it's because bool can be implicitly cast to int, and it somehow thinks I'm trying to do something like this: myClass[(int)myClass]

If I called this 'built in' operator[int, const char*] manually, what would happen? What is it there for?

My code currently works (by providing both (const char*) and (std::string) overloads for operator[]), but I'd like to understand why there was a problem in the first place.
Advertisement
Having the operator take an std::string and passing a char const * requires a conversion in order for the operator to be called, and likewise there is a conversion to the built in operator because of the implicit int conversion. Both paths are equally valid, and there is a therefore an ambiguity.

Having the operator take a char const * on the other hand requires no conversion for the operator to be called. Therefore, one path is more explicit that the other, and is chosen.
That would imply const char* can be implicitly cast to int. Is that so? Why?

Also, why is operator[](int, const char*) a built in function? What is it used for?
I re-read your question and comments on the built-in operator. I thought you knew about it, which is why I didn't explain more about it, but I misread your post on that part so I'll explain it. I was just too quick to read your question.

Basically, subscript syntax is quite liberal. The syntax a[c], when a is either a pointer or integer type and c is the other type, is equivalent to *(a+c). Think about that: it is not necessary for a to be the pointer and c to be the integer; it can be the other way around.
[source]
int main(int argc, char **argv)
{
for(int n=0; n<11; ++n)
{
std::cout << n["hello world"];
}
}
[/source]
Perfectly valid and well behaved code, and prints the characters of the string one by one.

What your int-operators allows for is this syntax; the object of type MyClass with an implicit conversion to integer on the left hand side, and the explicit pointer type on the right hand side. Same with bool operator, since it's an integer type and qualifies for this syntax.

The float operator however would require two conversions; the supplied MyClass to float, and a float to int conversion. Two conversions are not allows to qualify for a function call match and is therefore not an ambiguity.
Weird.

So the following work [s](though the first I find odd)[/s]:
std::cout << 5["xxxxxFxxx"];
std::cout << "xxLxxx" [2];

[s]But this does not:
std::cout << [3, "xxxMxx"];

So operator[](int, const char*) is not 'built-in' to MyClass itself, but a global function[/s] (I re-read your post)

So:
myClass["test"]

...is interpreted as:
int(myClass) ["test"] (with global ::operator[] for const char* string literals)

...and is unrelated to me having a MyClass::operator[], and just happenstance that the only time I'd ever use myClass[] is if I added operator[] to the class?

Weird.

So the following work (though the first I find odd):
std::cout << 5["xxxxxFxxx"];
std::cout << "xxLxxx" [2];

But this does not:
std::cout << [3, "xxxMxx"];

The first two are correct, but the second one does not follow the syntax of the subscript operator, which is a[c]; one to the left of the brackets and one inside them. You can use the function call syntax also if you want.
[source]
MyClass foo;
foo.operator[]("hello world");
[/source]
The global one would not be considered since it simply isn't the correct syntax (it's a member function call). This will not cause any ambiguity, since there is only one overloaded operator [] as a member function, but it kind of defeats the purpose of having the operator and not just a regular member function.


So operator[](int, const char*) is not 'built-in' to MyClass itself, but a global function?

That is correct. The problem was that one of the parameters was implicitly coming from the class due to the cast operator, which allowed for a match.


So:
myClass["test"]

...is interpreted as:
int(myClass) ["test"] (with global ::operator[] for const char* string literals)

...and is unrelated to me having a MyClass::operator[], and just happenstance that the only time I'd ever use myClass[] is if I added operator[] to the class?

That is also correct.

This thread shows is why cast operators can be dangerous. The wrong one was silently called under the correct condition, and otherwise you at least got an error that made no sense if you didn't knew about this subscript deal. Unless you really have a good reason for the cast operator, prefer an explicit function like std::string::c_str(), perhaps to_int() or something.
Alright, thank you, that makes sense. I'll just remove the implicit cast for safety, but I'm glad I actually now understand what was going on - it was really confusing me.

This topic is closed to new replies.

Advertisement