syntax for overloading operators

J

Jess

Hello,

After seeing some examples about operator overloading, I'm still a bit
confused about the general syntax. The following is what I think, not
sure whether it's correct.

1. For a unary operator that's a member of a class, its form is
usually

"operatorP()" (where P is the operator's name).

If it's a non-member operator, then its form is

"operatorP(arg)"

2.For a binary operator that's a member of a class, its form is:

operatorP(arg),

where arg is the right argument of P.

If it's a non-member operator, then its form is

"operatorP(argLeft,argRight)"

Is there anything I've missed? Moreover, can I overload a ternary
operator? I think there's only one ternary operator, which is "a ?
b:c".

Many thanks,
Jess
 
A

Alf P. Steinbach

* Jess:
After seeing some examples about operator overloading, I'm still a bit
confused about the general syntax. The following is what I think, not
sure whether it's correct.

1. For a unary operator that's a member of a class, its form is
usually

"operatorP()" (where P is the operator's name).

If it's a non-member operator, then its form is

"operatorP(arg)"

2.For a binary operator that's a member of a class, its form is:

operatorP(arg),

where arg is the right argument of P.

If it's a non-member operator, then its form is

"operatorP(argLeft,argRight)"

Is there anything I've missed?

For operator++ and operator-- you differentiate between prefix and
postfix forms via a dummy int second argument.

Moreover, can I overload a ternary operator?
No.


I think there's only one ternary operator, which is "a ? b:c".

Yes.
 
V

Victor Bazarov

Jess said:
After seeing some examples about operator overloading, I'm still a bit
confused about the general syntax. The following is what I think, not
sure whether it's correct.

1. For a unary operator that's a member of a class, its form is
usually

"operatorP()" (where P is the operator's name).

In literature you'll find '@' is used instead of 'P'.
If it's a non-member operator, then its form is

"operatorP(arg)"

2.For a binary operator that's a member of a class, its form is:

operatorP(arg),

where arg is the right argument of P.

If it's a non-member operator, then its form is

"operatorP(argLeft,argRight)"

Is there anything I've missed?

Not really. Overloaded operators can take const arguments as well,
so when it's a member, it can have the form 'rv operator@() const'.
Moreover, can I overload a ternary
operator?

No. Neither can you overload '.' (dot), nor 'sizeof'. Also you
probably shouldn't overload logical OR and logical AND (you may,
however).
I think there's only one ternary operator, which is "a ?
b:c".

Right.

V
 
J

Juha Nieminen

Victor said:
Also you
probably shouldn't overload logical OR and logical AND (you may,
however).

Is this a recommendation because the most common case where you
might want to overload || and && is to support expressions like:

MyClass a, b;
...
if(a || b) ...

and this is better done by instead defining an "operator bool()"
in 'MyClass'?
 
V

Victor Bazarov

Juha said:
Is this a recommendation because the most common case where you
might want to overload || and && is to support expressions like:

MyClass a, b;
...
if(a || b) ...

and this is better done by instead defining an "operator bool()"
in 'MyClass'?

I don't know what the most common case in which you might want to
overload || and &&. Honestly, I don't. I only know that if those
are overloaded, the logic essentially changes -- no more "short
circuit" evaluation.

V
 
P

*PaN!*

Juha Nieminen said:
Is this a recommendation because the most common case where you
might want to overload || and && is to support expressions like:

MyClass a, b;
...
if(a || b) ...

and this is better done by instead defining an "operator bool()"
in 'MyClass'?

IMHO the reason is the confusion that overloaded &&/|| operators cannot
shortcut: to pass arguments to the overloaded operation the compiler need to
evaluate both parameters, and this can be confusing if you expect that b is
not evaluated if a has some specific property...
 
P

*PaN!*

IMHO the reason is the confusion that overloaded &&/|| operators cannot
shortcut: to pass arguments to the overloaded operation the compiler need
to evaluate both parameters, and this can be confusing if you expect that
b is not evaluated if a has some specific property...

I should have reread the sentence... ok, as Victor said, is that you no
longer have shortcut evaluation ;)
 
C

Clark Cox

Is this a recommendation because the most common case where you
might want to overload || and && is to support expressions like:

MyClass a, b;
...
if(a || b) ...

and this is better done by instead defining an "operator bool()"
in 'MyClass'?

I would actually recommend against writing an "operator bool()" in
almost any circumstance. With the implicit conversion from bool to any
arithmetic type, some counterintuitive (at first glance) conversions
will be allowed by the compiler.
 
V

Victor Bazarov

Clark said:
I would actually recommend against writing an "operator bool()" in
almost any circumstance. With the implicit conversion from bool to any
arithmetic type, some counterintuitive (at first glance) conversions
will be allowed by the compiler.

To Juha:

.... and as a fairly common replacement for 'operator bool()' you might
consider 'operator void*()' which would return a null pointer in case
of 'false' and "true" otherwise (don't return 'this', others might
figure it out and try taking advantage of it).

V
 
J

Juha Nieminen

Clark said:
I would actually recommend against writing an "operator bool()" in
almost any circumstance. With the implicit conversion from bool to any
arithmetic type, some counterintuitive (at first glance) conversions
will be allowed by the compiler.

I thought the C++ standard forbids two implicit conversions to be
performed on the same type.

In other words, if you have a class A with an operator bool() defined,
this is valid:

A a;
bool b = a;

but this is not (and should give a compiler error):

A a;
int b = a;

because it would require *two* implicit conversions.

Thus I don't really see the problem you are referring to.
 
V

Victor Bazarov

Juha said:
I thought the C++ standard forbids two implicit conversions to be
performed on the same type.

In other words, if you have a class A with an operator bool()
defined, this is valid:

A a;
bool b = a;

but this is not (and should give a compiler error):

A a;
int b = a;

because it would require *two* implicit conversions.

Thus I don't really see the problem you are referring to.

Actually, a standard conversion *sequence* followed by a user-defined
conversion, followed by another standard conversion *sequence*, is
what's allowed (AFAICT). See 13.3.3.1.2/1.

V
 
T

Thomas J. Gritzan

Jess said:
Hello,

After seeing some examples about operator overloading, I'm still a bit
confused about the general syntax. The following is what I think, not
sure whether it's correct.

1. For a unary operator that's a member of a class, its form is
usually

"operatorP()" (where P is the operator's name).

If it's a non-member operator, then its form is

"operatorP(arg)"

Think of it this way:

For unary operators you have: operator@(arg),
for binary operators: operator@(left, right),

and if implemented as member function, the first (left) argument is
discarded because it is the implicit 'this' parameter instead.
Is there anything I've missed? Moreover, can I overload a ternary
operator? I think there's only one ternary operator, which is "a ?
b:c".

You missed the function call operator. It is always a member function and
looks like:

return_type operator() () // or:
return_type operator() (arg1) // or:
return_type operator() (arg1, arg2) // and so on...

Where you can have as many args as you want.

In addition, read here:
http://www.parashift.com/c++-faq-lite/operator-overloading.html
(especially 13.5)
 
O

Old Wolf

1. For a unary operator that's a member of a class, its form is
usually

"operatorP()" (where P is the operator's name).

If it's a non-member operator, then its form is

"operatorP(arg)"

There's a special case for the postfix operator++,
and operator-- of course, even though it is unary
it takes an extra 'dummy argument' so that it has
a different signature from the prefix operator.
 
J

James Kanze

... and as a fairly common replacement for 'operator bool()' you might
consider 'operator void*()' which would return a null pointer in case
of 'false' and "true" otherwise (don't return 'this', others might
figure it out and try taking advantage of it).

In the case of [io]stream, conversion to bool was considered
completely inacceptable because bool converts implicitly to
something for which << and >> are legal operators. Which in
turn can lead to some very surprising ambiguities and/or
overload resolutions. It's not, IMHO, generally a problem, if
the object in question doesn't support any arithmetic operators,
and would never be used in a context where the ambiguity might
occur.

I seem to recall too that there are contexts where void* is also
ambiguous (although I forget what), and that the "ideal"
solution was to return a pointer to a private class. But
whatever the problem was, it's never shown up in practice, at
least in my code, so I generally don't bother.
 
J

James Kanze

Jess schrieb:

[...]
You missed the function call operator. It is always a member function and
looks like:
return_type operator() () // or:
return_type operator() (arg1) // or:
return_type operator() (arg1, arg2) // and so on...

And user defined conversions, which while not really an operator
overload, has the syntax of one.

When considering operator overloading, one should not forget
that they are one of the most powerful tools ever invented for
obfuscation. When considering there use, the rule is: when in
doubt, don't. If your class is an abstraction of an arithmetic
type, of course, *not* overloading the usual arithmetic
operators is obfuscation, as anyone who's had to use user
defined arithmetic types in Java can attest; if it is an
abstraction of a pointer, of course, unary * and -> are a must,
and if it is an indexable container, []. Beyond that, << is
formatted output, and >> is formatted input, and anything else
is abuse.
 
P

Pete Becker

James said:
I seem to recall too that there are contexts where void* is also
ambiguous (although I forget what), and that the "ideal"
solution was to return a pointer to a private class. But
whatever the problem was, it's never shown up in practice, at
least in my code, so I generally don't bother.

I think the problem isn't ambiguity, but the perceived risk that someone
will write "delete obj;". If obj can be implicitly converted to void*,
the resulting pointer gets passed to operator delete.

The usual recommendation to cure this is to return a pointer to a member
of the class. That's not a valid argument to delete, so the compiler
will diagnose this error. There are several places in the current
working draft for C++0x that piously recite that the "unspecified
boolean type" should be a pointer to member.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
J

James Kanze

James Kanze wrote:
I think the problem isn't ambiguity, but the perceived risk that someone
will write "delete obj;". If obj can be implicitly converted to void*,
the resulting pointer gets passed to operator delete.
The usual recommendation to cure this is to return a pointer to a member
of the class. That's not a valid argument to delete, so the compiler
will diagnose this error. There are several places in the current
working draft for C++0x that piously recite that the "unspecified
boolean type" should be a pointer to member.

Hmmm. I seem to remember someone recommending using a pointer
to a private member class, for reasons of overload resolution,
but I don't remember the details. In the end, I can't remember
ever having had an ambiguity with void*, and I've never, ever
seen a case where someone accidentally wrote something like
"delete std::cout ;". (And I've been reviewing C++ code for
well over 15 years, and I've seen some pretty bad code.) So it
seems a case of adding complexity and obfuscation in order to
avoid and/or detect errors that never occur in practice.

In my own code (today at least), when I want a boolean type, I
spell it "bool". Including in conversion operators. About the
only time I'd make an exception is when the class also supports
arithmetic operators (where the conversion to int could come
into play), and conversions might be involved in those
arithmetic operations (because without conversions, there will
always be an exact match). In practice, in my code, that means
stream classes, and nothing else. (And since my binary stream
classes all derived from std::basic_ios, I don't have to write
it myself there, either.)

(Note that I do use the "pointer to private class" as an
argument to operator== for smart pointers. But the goal there
isn't to have a boolean type; the goal is to allow comparison
with a null pointer constant, but not with anything else. And I
don't like it, but I've not found a better solution.)
 
A

Alf P. Steinbach

* Pete Becker:
I think the problem isn't ambiguity, but the perceived risk that someone
will write "delete obj;". If obj can be implicitly converted to void*,
the resulting pointer gets passed to operator delete.

The usual recommendation to cure this is to return a pointer to a member
of the class. That's not a valid argument to delete, so the compiler
will diagnose this error. There are several places in the current
working draft for C++0x that piously recite that the "unspecified
boolean type" should be a pointer to member.

The problem is the implicit conversion to something that can be used as
bool. The solution is to not provide implicit conversion. Naming
things is good, and it's even recommended by the Bible (go forth and
name all in heaven and on earth and wherever your fancy takes you).

Cheers,

- Alf
 
P

Pete Becker

James said:
In the end, I can't remember
ever having had an ambiguity with void*, and I've never, ever
seen a case where someone accidentally wrote something like
"delete std::cout ;". (And I've been reviewing C++ code for
well over 15 years, and I've seen some pretty bad code.) So it
seems a case of adding complexity and obfuscation in order to
avoid and/or detect errors that never occur in practice.

There's definitely a tendency with inexperienced designers to try to
solve every problem they can think of.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top