Jump to content

  • Log In with Google      Sign In   
  • Create Account

ambiguous overload for 'operator[]'


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
6 replies to this topic

#1 Servant of the Lord   Crossbones+   -  Reputation: 21183

Like
0Likes
Like

Posted 17 January 2012 - 03:50 PM

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:

error: ambiguous overload for 'operator[]' in 'myClass["test"]'
main.cpp:11: note: candidates are: operator[](int, const char*) <built-in>
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.
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


Sponsor:

#2 Brother Bob   Moderators   -  Reputation: 8631

Like
0Likes
Like

Posted 17 January 2012 - 04:19 PM

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.

#3 Servant of the Lord   Crossbones+   -  Reputation: 21183

Like
0Likes
Like

Posted 17 January 2012 - 04:23 PM

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?
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#4 Brother Bob   Moderators   -  Reputation: 8631

Like
3Likes
Like

Posted 17 January 2012 - 04:31 PM

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.
int main(int argc, char **argv)
{
	for(int n=0; n<11; ++n)
	{
		std::cout << n["hello world"];
	}
}

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.

#5 Servant of the Lord   Crossbones+   -  Reputation: 21183

Like
0Likes
Like

Posted 17 January 2012 - 05:04 PM

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"];

So operator[](int, const char*) is not 'built-in' to MyClass itself, but a global function
(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?
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#6 Brother Bob   Moderators   -  Reputation: 8631

Like
2Likes
Like

Posted 17 January 2012 - 05:21 PM

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.
MyClass foo;
foo.operator[]("hello world");

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.

#7 Servant of the Lord   Crossbones+   -  Reputation: 21183

Like
0Likes
Like

Posted 17 January 2012 - 05:31 PM

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.
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS