# MSVC operator problem

This topic is 3772 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I tried using MS VC++ 2005 express, but resigned due to this error:
class X {
private:

int xxx;

public:

operator int *() { return &xxx; }
int operator[](int i) { return xxx; }

void test()
{
X x;
unsigned idx = 5;

x[idx];  // C2666
}
};

error C2666: 'X::operator []' : 2 overloads have similar conversions d:\documents and settings\rp\moje dokumenty\visual studio 2005\projects\con_test\con_test\con_test.cpp(15): could be 'int X::operator [](int)' or 'built-in C++ operator[(int *, unsigned int)' while trying to match the argument list '(X, unsigned int)' It compiles under Borland Turbo C++, gcc, DevC++. Does anyone know if it's correct by means of ANSI C++?

##### Share on other sites
VS2005 is correct, and this is why providing both operator[] and a pointer cast operator is a bad idea (typical example being a string class). What's happening is your operator[] is expecting a signed int whereas you're passing in an unsigned. Normally this would be fine, the unsigned is cast to a signed (with a warning on higher warning levels) and operator[] is called. When you add the pointer cast operator though you now have 2 possible matches that both require only 1 cast, thus the error about ambiguity.

What version of GCC are you using? If it's recent I'm surprised it's letting that through.

##### Share on other sites
The VC++ ambiguity error seems to me to be the sane behaviour, though I don't know what the standard behaviour is. I'd say it depends on whether the standard considers a[b] to be a binary expression operator (like +) or a member operator (like =).

As for the actual code, either the operator[] is redundant (it could be removed) or it has a different behaviour from casting to int* and subscripting into that buffer, at which point pain and misery due to implicit conversions are pretty much guaranteed in the near future.

##### Share on other sites
Maybe someone has a Comeau compiler to check it? :-)
I'd don't remember which version of gcc it is, but it came with openSUSE 10.2, so it's probably one of the newest.
As for the ambiguity, I understand why VC doesn't like it. The problem is, is it the way it should work. Theoretically, I could make:

class X {public:    int operator[](int idx} { // ... }    int& operator*() { // ... }    X operator+(int i) { // ... }};

and have ambiguity here too, since x[n] == *(x + n).
But that's not the point. If I have operator[] defined VC shouldn't use cast operator.

##### Share on other sites
Quote:
 Original post by joanusdmentiaVS2005 is correct, and this is why providing both operator[] and a pointer cast operator is a bad idea (typical example being a string class).

It's indeed for a string class. However it's not redundant, since I perform range checking in operator[] so I need separate operator const char * and operator[].

##### Share on other sites
Quote:
 Original post by rozz666If I have operator[] defined VC shouldn't use cast operator.

Exactly. But since you don't have operator[] defined, VC starts trying to make casts until it finds a matching definition. Since it finds two, it complains.

If you had the correct int * operator[](unsigned) defined, it would have been used. Expecting operator[](int) to be used in place of operator[](unsigned) is, to the compiler, the same as expecting operator[](int) to be used in place of operator[](const std::vector<enum {TRUE, FALSE, FILE_NOT_FOUND}> &): it will work at the cost of a type cast, whenever that cast is possible.

##### Share on other sites
Quote:
Original post by ToohrVyk
Quote:
 Original post by rozz666If I have operator[] defined VC shouldn't use cast operator.

Exactly. But since you don't have operator[] defined, VC starts trying to make casts until it finds a matching definition. Since it finds two, it complains.

If you had the correct int * operator[](unsigned) defined, it would have been used.

Having operator[](unsigned) defined is also ambiguous, isn't it?
There are still 2 possibilities.

##### Share on other sites
Quote:
 Original post by rozz666Having operator[](unsigned) defined is also ambiguous, isn't it?There are still 2 possibilities.

No. There is only one single possibility which may be used with exactly zero type casts, and that would be int X::operator[](unsigned). Any other possibilities require one or more type casts, and are thus excluded from the search. VC++ is perfectly happy with that, too.

The real question is whether type-casts should be attempted on the left-hand side of operator[] or not. Both Visual C++ and g++ agree on that point (because, if you remove the operator[] version, both will use the cast). Therefore, g++ has no excuse for not complaining (or barely mentioning) the ambiguity—the only two sane behaviours here are preventing type-casts to the left of operator[], or detecting ambiguity.

##### Share on other sites
Take a look at the STL's std::string,

1) Use it

2) Instead of overloading the pointer operator it has a function c_str(), not only does this make it more explicit what's happening, which I feel in this case is a good-thing, but it avoids the ambiguity.

##### Share on other sites
Quote:
Original post by ToohrVyk
Quote:
 Original post by rozz666Having operator[](unsigned) defined is also ambiguous, isn't it?There are still 2 possibilities.

No. There is only one single possibility which may be used with exactly zero type casts, and that would be int X::operator[](unsigned). Any other possibilities require one or more type casts, and are thus excluded from the search. VC++ is perfectly happy with that, too.

The real question is whether type-casts should be attempted on the left-hand side of operator[] or not. Both Visual C++ and g++ agree on that point (because, if you remove the operator[] version, both will use the cast). Therefore, g++ has no excuse for not complaining (or barely mentioning) the ambiguity—the only two sane behaviours here are preventing type-casts to the left of operator[], or detecting ambiguity.

Then I presume pointers have operator[] defined for all signed and unsigned integer types or at least int and unsigned?
But there's one thing I don't understand.
Suppose I have:

class X {public:    void f(unsigned);};class Y {public:    void f(int);    operator X();};void test(){    Y y;    y.f((unsigned) 5); // Y::f or X::f ?}

Do you see my point?
It's obvious that's Y::f. Why operator[] isn't working the same way?

##### Share on other sites
Quote:
 Original post by dmatterTake a look at the STL's std::string,1) Use it2) Instead of overloading the pointer operator it has a function c_str(), not only does this make it more explicit what's happening, which I feel in this case is a good-thing, but it avoids the ambiguity.

I can't use std::string, because I need some more functionality than it has. I know I could inherit from std::string, that wouldnt spare my much time implementing my string class. Moreover, I use expression templates for operator+, so I really have no benefit from using std::string.

##### Share on other sites
edit: didn't read closely enough

##### Share on other sites
Quote:
 Original post by rozz666Do you see my point?It's obvious that's Y::f. Why operator[] isn't working the same way?

I agree that it doesn't make sense—to me, <edit>implicit</edit> casting on the left-hand side of operator[] should be forbidden, as with all other binary operators which accept lvalues as their left members. Neither Visual C++ nor g++ manage to get this right, though—g++ is worse than VC in this regard, because it's not consistent: it acts as if casting was forbidden when it would create an ambiguity, and acts as if it was allowed otherwise.

##### Share on other sites
Quote:
Original post by ToohrVyk
Quote:
 Original post by rozz666Do you see my point?It's obvious that's Y::f. Why operator[] isn't working the same way?

I agree that it doesn't make sense—to me, <edit>implicit</edit> casting on the left-hand side of operator[] should be forbidden, as with all other binary operators which accept lvalues as their left members. Neither Visual C++ nor g++ manage to get this right, though—g++ is worse than VC in this regard, because it's not consistent: it acts as if casting was forbidden when it would create an ambiguity, and acts as if it was allowed otherwise.

So now, that we agree that it doesn't make sense, we need to find out what's the standard says about it. It means buying ANSI sheet or Comeau compiler, so I hope someone in this forum hopefully has one of these?

##### Share on other sites
Quote:
 Original post by rozz666So now, that we agree that it doesn't make sense, we need to find out what's the standard says about it. It means buying ANSI sheet or Comeau compiler, so I hope someone in this forum hopefully has one of these?

There's also the standard draft, which is freely available online.

In particular:

Quote:
 13.5.5 §1 operator[] shall be a non-static member function with exactly one parameter. It implements the subscripting syntax postfix-expression [ expression ] Thus, a subscripting expression x[y] is interpreted as x.operator[](y) for a class object x of type T if T::operator[](T1) exists and if the operator is selected as the best match function by the overload reso- lution mechanism (_over.match.best_).

And:

Quote:
 13.3.1.2§4 For the built-in assignment operators, conversions of the left operand are restricted as follows: --no temporaries are introduced to hold the left operand, and --no user-defined conversions are applied to the left operand to achieve a type match with the left-most parameter of a built-in can- didate.§5 For all other operators, no such restrictions apply.

Hm. So, for every possible conversion, the compiler determines if an user-defined or built-in operator exists, and chooses the least ambiguous one, and the = operator is restricted.

I have to leave now, so I'll let the rest of the reading to you. [smile]

##### Share on other sites
Quote:
Original post by ToohrVyk
Quote:
 Original post by rozz666So now, that we agree that it doesn't make sense, we need to find out what's the standard says about it. It means buying ANSI sheet or Comeau compiler, so I hope someone in this forum hopefully has one of these?

There's also the standard draft, which is freely available online.

In particular:

Quote:
 13.5.5 §1 operator[] shall be a non-static member function with exactly one parameter. It implements the subscripting syntax postfix-expression [ expression ] Thus, a subscripting expression x[y] is interpreted as x.operator[](y) for a class object x of type T if T::operator[](T1) exists and if the operator is selected as the best match function by the overload reso- lution mechanism (_over.match.best_).

And:

Quote:
 13.3.1.2§4 For the built-in assignment operators, conversions of the left operand are restricted as follows: --no temporaries are introduced to hold the left operand, and --no user-defined conversions are applied to the left operand to achieve a type match with the left-most parameter of a built-in can- didate.§5 For all other operators, no such restrictions apply.

Hm. So, for every possible conversion, the compiler determines if an user-defined or built-in operator exists, and chooses the least ambiguous one, and the = operator is restricted.

I have to leave now, so I'll let the rest of the reading to you. [smile]

Thanks, I'll check it out.

##### Share on other sites
g++ is right!

Quote:
 13.3.3.2§2 When comparing the basic forms of implicit conversion sequences (as defined in _over.best.ics_) --a standard conversion sequence (_over.ics.scs_) is a better conver- sion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and --a user-defined conversion sequence (_over.ics.user_) is a better conversion sequence than an ellipsis conversion sequence (_over.ics.ellipsis_).

So, the Xint* conversion is strictly worse than unsignedint, and there should be no ambiguity.

In the end, judging from our human difficulty to resolve the ambiguity, I'd say it's pretty dangerous to leave it this way.

##### Share on other sites
Quote:
 Original post by rozz666I can't use std::string, because I need some more functionality than it has.

Like what?

##### Share on other sites
Quote:
 Original post by rozz666I can't use std::string, because I need some more functionality than it has.

What kind of functionality?

Quote:
 I know I could inherit from std::string, that wouldnt spare my much time implementing my string class.

Have you tried? You might be surprised. (Also consider composition instead of inheritance.) If you have to get at a lower level (e.g. because of the next part...), at least consider if std::vector is useful for storage.

Quote:
 Moreover, I use expression templates for operator+, so I really have no benefit from using std::string.

To avoid multiple reallocation? Oh, that sounds like a pretty major project. x.x

##### Share on other sites
Quote:
 Original post by rozz666I can't use std::string, because I need some more functionality than it has. I know I could inherit from std::string, that wouldnt spare my much time implementing my string class. Moreover, I use expression templates for operator+, so I really have no benefit from using std::string.

If you need to extend a standard container then aggregate it or write some custom algorithms.
You can let std::string handle 90% of the string management for you. If you think about it with std::string all the memory handling and most of the funtionality is already there, you can use that and just build from there.

Example aggregation:
class MyString{public:    // Extra functionality.protected:    std::string _string;};

##### Share on other sites
I use my own array class with reference counting for memory management. I don't realy need std::string. It's all written anyway. I added conversions to all numeric types operator+ and += for them. operator+ is written using expression templates to avoid multiple allocations.

It's a research project. I tried compiling it under VC++ because I heard that it has better optimizations than Borland. But anyways, the target OS is QNX so now I just got one more reason not to use VC++ :-) Despite being slower, I think Borland has also been doingthings according to standards unlike MS.

##### Share on other sites

This topic is 3772 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Create an account or sign in to comment

You need to be a member in order to leave a comment

## Create an account

Sign up for a new account in our community. It's easy!

Register a new account

• ### Forum Statistics

• Total Topics
628639
• Total Posts
2983972

• 10
• 18
• 20
• 13
• 9