By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,165 Members | 1,109 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,165 IT Pros & Developers. It's quick & easy.

Basic question on operator overloading

P: n/a
The following code was written by a colleague -- to preserve
confidentiality, the name of the class is changed:

HisClass operator+ (const HisClass & h1, const HisClass & h2)
{ // some code here}

HisClass HisClass::operator+ (const HisClass& h2)
{// more code here}
To me, this code appears to be in error because it appears to make
expressions such as h1 + h2 ambiguous.

It does compile though.

I'd like to run it by the newsgroup because it's an established part
of our library and I'd be a bit surprised to see a fundamental error
of this nature.

Am I missing something?

After writing this, I can now guess what happens. Is it the case that
only the 2nd version {//more code here} ever gets called because its
scope is narrower?
Thanks,

Paul Epstein

Jul 13 '07 #1
Share this Question
Share on Google+
8 Replies


P: n/a
pa**********@att.net wrote:
The following code was written by a colleague -- to preserve
confidentiality, the name of the class is changed:

HisClass operator+ (const HisClass & h1, const HisClass & h2)
{ // some code here}

HisClass HisClass::operator+ (const HisClass& h2)
{// more code here}
To me, this code appears to be in error because it appears to make
expressions such as h1 + h2 ambiguous.

It does compile though.

I'd like to run it by the newsgroup because it's an established part
of our library and I'd be a bit surprised to see a fundamental error
of this nature.

Am I missing something?

After writing this, I can now guess what happens. Is it the case that
only the 2nd version {//more code here} ever gets called because its
scope is narrower?
Well, it might be a little tricky to get at the non-member operator,
however, I think it is possible through conversion operators:

#include <iostream>
#include <ostream>
struct A {

A operator+ ( A const & other ) {
std::cout << "member\n";
return ( *this );
}

void member ( void ) {
*this + *this;
}

};

A operator+ ( A const & lhs, A const & rhs ) {
std::cout << "free\n";
return ( lhs );
}

struct B : public A {};

struct C {

operator A ( void ) {
return A();
}

};

int main ( void ) {
A a1;
A a2;

a1.member();
a1 + a2;

B b1;
B b2;
b1.member();
b1 + b2;
a1 + b1;
b1 + a1;

C c1;
C c2;

c1 + c2; // free version
a1 + c1;
c1 + a1; // free version

}
Best

Kai-Uwe Bux
Jul 13 '07 #2

P: n/a
On Jul 13, 10:18 am, Kai-Uwe Bux <jkherci...@gmx.netwrote:
pauldepst...@att.net wrote:
The following code was written by a colleague -- to preserve
confidentiality, the name of the class is changed:
HisClass operator+ (const HisClass & h1, const HisClass & h2)
{ // some code here}
HisClass HisClass::operator+ (const HisClass& h2)
{// more code here}
To me, this code appears to be in error because it appears to make
expressions such as h1 + h2 ambiguous.
It does compile though.
I'd like to run it by the newsgroup because it's an established part
of our library and I'd be a bit surprised to see a fundamental error
of this nature.
Am I missing something?
After writing this, I can now guess what happens. Is it the case that
only the 2nd version {//more code here} ever gets called because its
scope is narrower?

Well, it might be a little tricky to get at the non-member operator,
however, I think it is possible through conversion operators:

#include <iostream>
#include <ostream>

struct A {

A operator+ ( A const & other ) {
std::cout << "member\n";
return ( *this );
}

void member ( void ) {
*this + *this;
}

};

A operator+ ( A const & lhs, A const & rhs ) {
std::cout << "free\n";
return ( lhs );

}

struct B : public A {};

struct C {

operator A ( void ) {
return A();
}

};

int main ( void ) {
A a1;
A a2;

a1.member();
a1 + a2;

B b1;
B b2;
b1.member();
b1 + b2;
a1 + b1;
b1 + a1;

C c1;
C c2;

c1 + c2; // free version
a1 + c1;
c1 + a1; // free version

}

Best

Kai-Uwe Bux- Hide quoted text -

- Show quoted text -
Thanks Kai-Uwe

As a relative newbie, I don't quite understand your code.

In particular, I don't fully understand struct C.

How can you be sure in your code that the free version is being
called?

Do you have a good (preferably free online) text which covers this
issue?

However, I do think that I spotted a problem that was simply
overlooked by my colleague, and I believe that the non-member version
should just be commented out.

Thanks again,

Paul Epstein

Jul 13 '07 #3

P: n/a
Alf P. Steinbach wrote:
* pa**********@att.net:
>The following code was written by a colleague -- to preserve
confidentiality, the name of the class is changed:

HisClass operator+ (const HisClass & h1, const HisClass & h2)
{ // some code here}

Since operator+ can at most have two arguments, the above cannot be a
function definition in the class definition. Hence it's a free-standing
definition of operator+. Not a member function.
>HisClass HisClass::operator+ (const HisClass& h2)
{// more code here}

This one is a member function, and duplicates the functionality of the
former. However, it's not exactly the same: the formal argument for the
left hand side of h1+h2 is non-const.
>To me, this code appears to be in error because it appears to make
expressions such as h1 + h2 ambiguous.
Just a nit:
h1+h2 will select the member function unless h1 is const
agreed
or temporary,
I would expect a temporary to force the member version since temporaries
cannot bind to the const & parameters of the free-standing version.
in which case the free-standing implementation is selected.
[snip]

Best

Kai-Uwe Bux
Jul 13 '07 #4

P: n/a
Alf P. Steinbach wrote:
* pa**********@att.net:
[on the issue as to whether operator+ better be a member or free-standing]
>Perhaps you haven't seen enough of the code to form an opinion, but I
believe that the member version should be commented out/ deleted.
(Before, I said this about the non-member version.)

Lack of code doesn't prevent me from forming an opinion on that, but
Kai-Uwe Bux (IIRC) argued earlier (half a year ago? earlier?) that it
actually made sense to have operator+= as non-member and operator+ as
member. And so, now I'm not sure which to prefer. This choice might be
akin to the choice of some particular style of formatting, in that it
can be argued at length, without any clear conclusion.
IIRC, the discussion back then was on the issue whether operator+ better be
defined in terms of operator+= or vice versa.

With respect to the issue currently under discussion, I would favor (as of
today) a free-standing version declared outside class scope. It is more
symmetric with regard to conversions (argued elsethread). E.g.:
#include <iostream>
#include <ostream>

struct A {

friend
A operator+ ( A const & lhs, A const & rhs ) {
std::cout << "friend\n";
return ( lhs );
}

};

// the following line pulls the function into global scope:
A operator+ ( A const & lhs, A const & rhs );

struct C {

operator A ( void ) {
return A();
}

};

int main ( void ) {
C c1;
C c2;
c1 + c2; // this depends on global scope
}
BTW, I wonder why the above compiles. To me, it looks as though the
conversion function should create non-const temporaries which should not
bind to the parameters. Apparently, that is an illusion and implicit
conversions are different.

Best

Kai-Uwe Bux
Jul 13 '07 #5

P: n/a
Alf P. Steinbach wrote:
* Kai-Uwe Bux:
>>
I would expect a temporary to force the member version since temporaries
cannot bind to the const & parameters of the free-standing version.

A temporary can be bound to a reference to const (free-standing version).

A temporary cannot be bound to a reference to non-const.

And so I thought you were entirely wrong, but as it turns out, according
to MSVC and g++ compilers your conclusion is correct (and I was wrong),
but your reason is incorrect: remove the member function and all is
fine, because temporaries can indeed bind to reference to const.

The true reason lies in the section of the standard you cited
else-thread (§13.3.1/5, also mentioned elsewhere in the standard),
namely that a non-const member function can be called on a temporary.

So, sorry, I was wrong about that, hasty conclusion, but note that your
reason above is also wrong.
Right, the reason I gave is total garbage. I did run the hypothesis by my
compiler, which chose the free-standing version and my mind made up a
totally bogus explanation. My bad.
Best

Kai-Uwe Bux
Jul 13 '07 #6

P: n/a
On Jul 13, 4:18 am, Kai-Uwe Bux <jkherci...@gmx.netwrote:
pauldepst...@att.net wrote:
The following code was written by a colleague -- to preserve
confidentiality, the name of the class is changed:
HisClass operator+ (const HisClass & h1, const HisClass & h2)
{ // some code here}
HisClass HisClass::operator+ (const HisClass& h2)
{// more code here}
To me, this code appears to be in error because it appears to make
expressions such as h1 + h2 ambiguous.
No, because in the second, the "implicit this" operand is
non-const. Const can be used for overload resolution; the
compiler will normally prefer the non-const version if it can be
called.
It does compile though.
I'd like to run it by the newsgroup because it's an established part
of our library and I'd be a bit surprised to see a fundamental error
of this nature.
Am I missing something?
Not really. I can't think of a case where it would be
appropriate.

Are you sure that the member isn't +=, rather than just +. That
is a more or less standard idiom: += should be a member, and + a
free function.
After writing this, I can now guess what happens. Is it the case that
only the 2nd version {//more code here} ever gets called because its
scope is narrower?
The way operator lookup works, both will be considered. If both
can be called, the member will be prefered, because of the rule
of preferring the least const-qualified. Because the member is
const, however, there are cases where it cannot be called, in
which case, the free function will be called.
Well, it might be a little tricky to get at the non-member operator,
Not tricky at all:

HisClass
f( HisClass const& a, HisClass const& b )
{
return a + b ;
}

The member cannot be called, since it is non-const.

I suspect, however, that either the original poster misread, and
took a += for a +, or the author mis-copied code using a member
+= and a non member +.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Jul 13 '07 #7

P: n/a
On Jul 13, 4:48 am, "Alf P. Steinbach" <al...@start.nowrote:
* pauldepst...@att.net:
The following code was written by a colleague -- to preserve
confidentiality, the name of the class is changed:
HisClass operator+ (const HisClass & h1, const HisClass & h2)
{ // some code here}
Since operator+ can at most have two arguments, the above cannot be a
function definition in the class definition. Hence it's a free-standing
definition of operator+. Not a member function.
HisClass HisClass::operator+ (const HisClass& h2)
{// more code here}
This one is a member function, and duplicates the functionality of the
former. However, it's not exactly the same: the formal argument for the
left hand side of h1+h2 is non-const.
To me, this code appears to be in error because it appears to make
expressions such as h1 + h2 ambiguous.
h1+h2 will select the member function unless h1 is const or temporary,
in which case the free-standing implementation is selected.
I think it can be a temporary; something like:

HisClass f() ;
f() + h2 ;

should still select the member. On the other hand, no implicit
conversion (except Derived to Base?) will be considered to make
the types match.
Assuming the code is correctly illustrated above, I think that
is not intentional.
I suspect the intent was that the member be in fact operator+=.
If the member were in fact +=, then we have a very common idiom.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Jul 13 '07 #8

P: n/a
On Jul 13, 5:43 am, "Alf P. Steinbach" <al...@start.nowrote:
* pauldepst...@att.net:
[...]
I thought that scoping considerations overrode
matching-parameter considerations.
No. First all viable functions are collected in one big bowl.
Then the compiler checks parameter matching for all of them,
on equal footing.
I think you're one step ahead of him. Name lookup occurs first,
and only functions which are found during name looup will be
considered when constructing the list of viable functions.

For a named function, the issue is rather clear: if the
expression is of the form p->f() or o.f(), name lookup will
occur only in class scope, and free functions aren't considered.
If the expression is of the form f(), and we are not in a member
function, name lookup will not consider members, and only a
free function can be found. If the expression is of the form
f(), and we are in a member function, name lookup will first
look in the class, and if it finds the function there, will not
look further.

For an operator, function overload resolution proceeds more or
less as it does for a function. But given a + b, the compiler
doesn't know whether you meant "operator+( a, b )" or
"a.operator+( b )", so it does both lookups, and merges the
results. (It also adds synthesized declarations for the
built-in operators, systematically, so these cannot be hidden.)
So I'm surprised that which version gets called can depend on a
constness issue.
Also, I'm a bit confused by "unless h1 is const or temporary".
A const or temporary cannot be bound to a reference to non-const (except
with Microsoft's Visual C++, language extension).
Yes, but there are no references to non-const in his example.
You can call a non-const member function on a temporary; for
overload resolution purposes, the function will behave as if it
had an additional, reference argument, this is only for
overloading purposes, and doesn't affect the viability of the
function. (How often have people asked why `ostringstream() <<
"toto"' displays an address, and not the string "toto". The
reason, of course, is that the operator<< for void const* is a
member, and so can be called, but the operator<< for char const*
is a free function, and can't.)
I would not have thought it matters whether h1 is temporary or not
because I don't see how the compiler can recognise temporary
variables.
This is a temporary as lhs:
HisClass() + h2;
And should call the member operator+.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Jul 13 '07 #9

This discussion thread is closed

Replies have been disabled for this discussion.