Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Clashes between type conversion and operator[]

0 views
Skip to first unread message

Arne Schmitz

unread,
Nov 9, 2005, 1:06:41 PM11/9/05
to
I guess this has been asked before, but I cannot find any answer to this
problem. I have program like this:

---SNIP---
#include <cassert>
#include <cstdlib>

class C
{
public:

inline operator float*() { return v; }

inline float& operator[](size_t i) { assert(i < 3); return v[i]; }
inline const float& operator[](size_t i) const
{ assert(i < 3); return v[i]; }

private:

float v[3];

};

int main()
{
C c;

c[1] = 0.0f;

return 0;
}
---SNIP---

Now, this does not compile, because the float* and the [] clash, since []
uses size_t (or unsigned) and the operator[] implicitly defined by the
float* conversion, also uses size_t, as far as I understand. Is there a way
to define both operators at the same time, while keeping the size_t of
operator[](size_t)?

Arne

--
[--- PGP key FD05BED7 --- http://www.root42.de/ ---]

Victor Bazarov

unread,
Nov 9, 2005, 1:21:42 PM11/9/05
to

No, as you have discovered.

Drop the type conversion operator and use a normal, named, function:

float* get_v() { return v; }

V

Andrey Tarasevich

unread,
Nov 9, 2005, 5:09:21 PM11/9/05
to
Arne Schmitz wrote:
> I guess this has been asked before, but I cannot find any answer to this
> problem. I have program like this:
>
> ---SNIP---
> #include <cassert>
> #include <cstdlib>
>
> class C
> {
> public:
>
> inline operator float*() { return v; }
>
> inline float& operator[](size_t i) { assert(i < 3); return v[i]; }
> inline const float& operator[](size_t i) const
> { assert(i < 3); return v[i]; }
>
> private:
>
> float v[3];
>
> };
>
> int main()
> {
> C c;
>
> c[1] = 0.0f;
>
> return 0;
> }
> ---SNIP---
>
> Now, this does not compile, because the float* and the [] clash, since []
> uses size_t (or unsigned) and the operator[] implicitly defined by the
> float* conversion, also uses size_t, as far as I understand.

Not exactly. The built-in '[]' operator takes a _signed _ argument, namely - an
argument or 'ptrdiff_t' type.

The reason you have a clash in this case is that the 'c[1]' expression can be
interpreted in two different ways (as you correctly noted) and there's no clear
winner between the two.

Subscript operator has two parameters: "the pointer" and "the index".

The approach that uses the user defined conversion to 'float*' and the built-in
'[]' operator requires a user-defined conversion for the first argument and
requires no conversion for the second argument (or might require an integral
promotion 'int' -> 'ptrdiff_t').

The approach that uses the user-defined operator '[]' needs no conversion for
the first argument and requires an integral conversion ('int' -> 'size_t') for
the second argument.

According to the language specification neither approach is better than the
other one.

> Is there a way
> to define both operators at the same time, while keeping the size_t of
> operator[](size_t)?

Probably no. Either change the parameter of the user defined '[]' operator to
have type 'ptrdiff_t'. Or remember to always explicitly use an unsigned value as
an index

c[1U] = 0.0f;

--
Best regards,
Andrey Tarasevich

Andrey Tarasevich

unread,
Nov 9, 2005, 5:33:13 PM11/9/05
to
Andrey Tarasevich wrote:
> ...

> Probably no. Either change the parameter of the user defined '[]' operator to
> have type 'ptrdiff_t'. Or remember to always explicitly use an unsigned value as
> an index
>
> c[1U] = 0.0f;
> ...

... although the latter is not guaranteed to resolve the ambiguity in general case.

Arne Schmitz

unread,
Nov 10, 2005, 6:55:46 AM11/10/05
to
Andrey Tarasevich wrote:

>> Is there a way
>> to define both operators at the same time, while keeping the size_t of
>> operator[](size_t)?
>
> Probably no. Either change the parameter of the user defined '[]' operator
> to have type 'ptrdiff_t'. Or remember to always explicitly use an unsigned
> value as an index
>
> c[1U] = 0.0f;

The first sounds good, but why e.g. does std::vector use size_t as the index
for operator[]? Are there any pros/cons?

The latter is too tedious for the programmer, I think. One can easily forget
the suffix U.

Andrey Tarasevich

unread,
Nov 10, 2005, 1:04:27 PM11/10/05
to
Arne Schmitz wrote:
> Andrey Tarasevich wrote:
>
>>> Is there a way
>>> to define both operators at the same time, while keeping the size_t of
>>> operator[](size_t)?
>>
>> Probably no. Either change the parameter of the user defined '[]' operator
>> to have type 'ptrdiff_t'. Or remember to always explicitly use an unsigned
>> value as an index
>>
>> c[1U] = 0.0f;
>
> The first sounds good, but why e.g. does std::vector use size_t as the index
> for operator[]? Are there any pros/cons?

'std::vector<>' uses 'std::vector<>::size_type' as an index (which, of course,
might be the same as 'std::size_t'). The 'std::vector's operator '[]' doesn't
need negative indexing and doesn't suffer form the ambiguity in question, which
makes it perfectly logical to choose an unsigned type for indexing.

Actually, I'd say that this applies to your situation as well. You don't need
negative indexing and therefore it makes more sense to stick with an unsigned
index type ('size_t' in your case). I wouldn't really recommend switching to a
signed type just to attempt to resolve the overloading ambiguity. In my previous
message I used the "workaround" with 'ptrdiff_t' just to illustrate how the
overload resolution works.

A more reasonable solution would be to get rid of the ambiguity by replacing the
'float*' conversion operator with a regular function (see Victor's message) or,
maybe, by removing it entirely. (Do you really need it?)

Arne Schmitz

unread,
Nov 14, 2005, 8:46:26 AM11/14/05
to
Andrey Tarasevich wrote:

> 'std::vector<>' uses 'std::vector<>::size_type' as an index (which, of
> course, might be the same as 'std::size_t'). The 'std::vector's operator
> '[]' doesn't need negative indexing and doesn't suffer form the ambiguity
> in question, which makes it perfectly logical to choose an unsigned type
> for indexing.

Yes, makes sense.



> Actually, I'd say that this applies to your situation as well. You don't
> need negative indexing and therefore it makes more sense to stick with an
> unsigned index type ('size_t' in your case). I wouldn't really recommend
> switching to a signed type just to attempt to resolve the overloading
> ambiguity. In my previous message I used the "workaround" with 'ptrdiff_t'
> just to illustrate how the overload resolution works.

True.

> A more reasonable solution would be to get rid of the ambiguity by
> replacing the 'float*' conversion operator with a regular function (see
> Victor's message) or, maybe, by removing it entirely. (Do you really need
> it?)

No, I do not need it, although I bet quite a few programs here depend on the
'float*' operator. But they will have to adapt. :-)

Thank you for the very good explanations.

0 new messages