473,695 Members | 1,901 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

operator+ overload resolution

Hi,

I have the following code for example:

/*************** *************** ****/

#include <iostream>

class C1 {
public:
int a;

C1(int b) : a(b) { }

int operator+(C1& o) {
std::cout << "C1 Op+ CALLED";
return a + o.a;
}
};

class C2 : public C1 {
public:
C2(int b) : C1(b) { }

operator int() const { return a; }

int operator+(int o) {
std::cout << "C2 Op+ CALLED";
return o + a;
}
};

int main(int argc, char** argv) {
C2 o1(2);
C2 o2(3);

C2 o3 = o1 + o2;

return 0;
}

/*************** *************** ****/

Surprisingly this compiles (with g++) and outputs 'C2 Op+ CALLED'.
However, surely this should not compile since the call to operator+ is
surely ambiguous? int operator+(int o) called with an O2 requires one
cast (C2 -> int, which is defined explicitly in the class) and so does
int operator+(C1& o) (C2& -> C1&). What's going on here? Have I
misunderstood function overload resolution? Is g++ implementing the
standard incorrectly?

Thanks,

Richard Hayden.
Jul 22 '05 #1
7 2088
Richard Hayden wrote in news:cc******** **@hercules.bti nternet.com in
comp.lang.c++:
class C1 { int operator+(C1& o) { };

class C2 : public C1 { int operator+(int o) { };

int main(int argc, char** argv) {
C2 o1(2);
C2 o2(3);

C2 o3 = o1 + o2;

return 0;
}

/*************** *************** ****/

Surprisingly this compiles (with g++) and outputs 'C2 Op+ CALLED'.
However, surely this should not compile since the call to operator+ is
surely ambiguous? int operator+(int o) called with an O2 requires one
cast (C2 -> int, which is defined explicitly in the class) and so does
int operator+(C1& o) (C2& -> C1&). What's going on here? Have I
misunderstood function overload resolution? Is g++ implementing the
standard incorrectly?


Your analysis is missing a conversion from C2 to C1, you're analysing
the right hand side only and ignoring the left hand side:

operator +( C2, C2 ) is

A) C2::operator +( C2, C2 |-> int ) or

B) C1::operator +( C2 |-> C1, C2 |-> C1 )

(A) requires a shorter conversion sequence in the first paramiter
to operator + and is choosen.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #2
Richard Hayden wrote:
...
I have the following code for example:

/*************** *************** ****/

#include <iostream>

class C1 {
public:
int a;

C1(int b) : a(b) { }

int operator+(C1& o) {
std::cout << "C1 Op+ CALLED";
return a + o.a;
}
};

class C2 : public C1 {
public:
C2(int b) : C1(b) { }

operator int() const { return a; }

int operator+(int o) {
std::cout << "C2 Op+ CALLED";
return o + a;
}
};

int main(int argc, char** argv) {
C2 o1(2);
C2 o2(3);

C2 o3 = o1 + o2;

return 0;
}

/*************** *************** ****/

Surprisingly this compiles (with g++) and outputs 'C2 Op+ CALLED'.
However, surely this should not compile since the call to operator+ is
surely ambiguous? int operator+(int o) called with an O2 requires one
cast (C2 -> int, which is defined explicitly in the class) and so does
int operator+(C1& o) (C2& -> C1&). What's going on here? Have I
misunderstood function overload resolution? Is g++ implementing the
standard incorrectly?
...


According to 13.3.1.2/3, the set of member candidates in this case is
created by qualified lookup for 'C2::operator+' . But this lookup will
not find the inherited 'C1::operator+' in 'C2' simply because it is
hidden in 'C2'. I.e. 'C1::operator+' is not even considered as a viable
function. For this reason there's no ambiguity.

The potential for ambiguity will appear if you add a 'using' declaration
of 'C1::operator+' to 'C2's definition

using C1::operator+;

In this case 'C1::operator+' will be found by name lookup and added to
the set of viable functions. Calling this function will require two
implicit conversions of 'reference binding' type (see 13.3.3.1.4): one
for the implied object argument and another for the explicit argument of
'C1::operator+' . Both bind a 'C1&' to an object of 'C2' type. To me it
looks like it should result in an ambiguity. Such bindings are given the
'Conversion' rank, which means that 'C1::operator+' requires worse
conversion sequence for the implied object argument than 'C2::operator+'
(which requires no conversion). At the same time 'C2::operator+'
requires worse conversion sequence (user-defined) for the explicit
argument than 'C1::operator+' .

However, Comeau accepts the modified code and chooses 'C1::operator+'
(!). Maybe I'm missing something...

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #3
Rob Williscroft wrote in news:Xns952116F 6E7409ukcoREMOV Efreenetrtw@
130.133.1.4 in comp.lang.c++:
Your analysis is missing a conversion from C2 to C1, you're analysing
the right hand side only and ignoring the left hand side:

operator +( C2, C2 ) is

A) C2::operator +( C2, C2 |-> int ) or

B) C1::operator +( C2 |-> C1, C2 |-> C1 )

(A) requires a shorter conversion sequence in the first paramiter
to operator + and is choosen.


And I missed that C2::operator + hiddes C1::operator +,
fortunatly Andrey Tarasevich didn't miss it.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #4
On Thu, 08 Jul 2004 18:39:42 -0700, Andrey Tarasevich
<an************ **@hotmail.com> wrote:

[...]

According to 13.3.1.2/3, the set of member candidates in this case is
created by qualified lookup for 'C2::operator+' . But this lookup will
not find the inherited 'C1::operator+' in 'C2' simply because it is
hidden in 'C2'. I.e. 'C1::operator+' is not even considered as a viable
function. For this reason there's no ambiguity.

The potential for ambiguity will appear if you add a 'using' declaration
of 'C1::operator+' to 'C2's definition

using C1::operator+;

In this case 'C1::operator+' will be found by name lookup and added to
the set of viable functions. Calling this function will require two
implicit conversions of 'reference binding' type (see 13.3.3.1.4): one
for the implied object argument and another for the explicit argument of
'C1::operator+ '. Both bind a 'C1&' to an object of 'C2' type. To me it
looks like it should result in an ambiguity. Such bindings are given the
'Conversion' rank, which means that 'C1::operator+' requires worse
conversion sequence for the implied object argument than 'C2::operator+'
(which requires no conversion). At the same time 'C2::operator+'
requires worse conversion sequence (user-defined) for the explicit
argument than 'C1::operator+' .

My standard is not currently is not within arm's reach, nonetheless
I'm not sure if I'm following the meaning behind 'worse conversion
sequence' for the implied and the explict object.

For C2 worse than C1 (the explicit argument), the fact that the OP
only defined operator+ with an int parameter, required a casts of o2
to an int to perform the addition. Yes/No? If yes, how's C2 worse
than C1?

For C1 worse than C2, perhaps I'm misunderstandin g the meaning of
'implied object argument'. Elaborate if you will.

Thanks

Mark
--
[ C++ FAQ: http://www.parashift.com/c++-faq-lite/ ]
Jul 22 '05 #5
Mark wrote:

According to 13.3.1.2/3, the set of member candidates in this case is
created by qualified lookup for 'C2::operator+' . But this lookup will
not find the inherited 'C1::operator+' in 'C2' simply because it is
hidden in 'C2'. I.e. 'C1::operator+' is not even considered as a viable
function. For this reason there's no ambiguity.

The potential for ambiguity will appear if you add a 'using' declaration
of 'C1::operator+' to 'C2's definition

using C1::operator+;

In this case 'C1::operator+' will be found by name lookup and added to
the set of viable functions. Calling this function will require two
implicit conversions of 'reference binding' type (see 13.3.3.1.4): one
for the implied object argument and another for the explicit argument of
'C1::operator +'. Both bind a 'C1&' to an object of 'C2' type. To me it
looks like it should result in an ambiguity. Such bindings are given the
'Conversion ' rank, which means that 'C1::operator+' requires worse
conversion sequence for the implied object argument than 'C2::operator+'
(which requires no conversion). At the same time 'C2::operator+'
requires worse conversion sequence (user-defined) for the explicit
argument than 'C1::operator+' .
My standard is not currently is not within arm's reach, nonetheless
I'm not sure if I'm following the meaning behind 'worse conversion
sequence' for the implied and the explict object.

For C2 worse than C1 (the explicit argument), the fact that the OP
only defined operator+ with an int parameter, required a casts of o2
to an int to perform the addition. Yes/No? If yes, how's C2 worse
than C1?


'C2::operator+' is worse than 'C1::operator+' because in case of
'C2::operator+' a conversion from explicit argument type 'C2' to
explicit parameter type 'int' is required. This can only be done by a
user-defined conversion in this case.

In case of 'C1::operator+' another conversion is required: from explicit
argument type 'C2' to explicit parameter type 'C1&'. As far as I
understand the standard, this conversion is treated as a standard one
(even though it is not included in the list of "official" standard
conversions). For this reason 'C1::operator+' is "better" than
'C2::operator+' (standard conversions are always "better" than
user-defined ones).
For C1 worse than C2, perhaps I'm misunderstandin g the meaning of
'implied object argument'. Elaborate if you will.


Candidate member functions of class 'T' are considered to have an extra
parameter referred to as 'implied object parameter'. This parameter has
type 'T&' (or 'const/volatile T&' for const/volatile member functions).
This parameter is bound to the object, for which the member function has
been invoked. (That's what I referred to as 'implied object argument'.
Not a good choice of words. The argument is not really 'implied'. I
should've called it simply 'object argument'). Conversions that are
required in order to bind this parameter are also taken into account by
overload resolution. Once again, if I understood the standard correctly,
in this case the implied object parameter for 'C1::operator+' has type
'C1&' and the object has type 'C2', i.e. a conversion from 'C2' to 'C1&'
is required (ranked as 'Conversion'). The implied object parameter for
'C2::operator+' has type 'C2&' and and the object has type 'C2', i.e. no
conversion is required (ranked as 'Exact match'). That's why
'C2::operator+' is "better" from this point of view ('Exact
match'-ranked conversions are always "better" than 'Conversion'-ranked
ones).

It possible that the above explanation contains some mistakes caused by
my misunderstandin g of some portions of the standard. Comeau's behavior
also suggests that. It would be great to get to the bottom of this issue.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #6
Andrey Tarasevich wrote in news:10******** *****@news.supe rnews.com in
comp.lang.c++:
According to 13.3.1.2/3, the set of member candidates in this case is
created by qualified lookup for 'C2::operator+' . But this lookup will
not find the inherited 'C1::operator+' in 'C2' simply because it is
hidden in 'C2'. I.e. 'C1::operator+' is not even considered as a
viable function. For this reason there's no ambiguity.

The potential for ambiguity will appear if you add a 'using'
declaration of 'C1::operator+' to 'C2's definition

using C1::operator+;

In this case 'C1::operator+' will be found by name lookup and added to
the set of viable functions. Calling this function will require two
implicit conversions of 'reference binding' type (see 13.3.3.1.4): one
for the implied object argument and another for the explicit argument
of 'C1::operator+' . Both bind a 'C1&' to an object of 'C2' type. To me
it looks like it should result in an ambiguity. Such bindings are
given the 'Conversion' rank, which means that 'C1::operator+' requires
worse conversion sequence for the implied object argument than
'C2::operator+' (which requires no conversion). At the same time
'C2::operator+' requires worse conversion sequence (user-defined) for
the explicit argument than 'C1::operator+' .

However, Comeau accepts the modified code and chooses 'C1::operator+'
(!). Maybe I'm missing something...


This is a good question, last night I just didn't know the answer, this
morning, I thought "but isn't a (member) using declaration a bit like":

class B
{
public:
void f();
};

class D : B
{
public:
// using B::f;
void f () { B::f(); }
}

It took me about 1/2 an hour to find it in the standard:

7.3.3/13: (Theusingdeclar ation)

For the purpose of overload resolution, the functions which are
introduced by a using-declaration into a derived class will be treated
as though they were members of the derived class. In particular, the
implicit this parameter shall be treated as if it were a pointer to the
derived class rather than to the base class. This has no effect on the
type of the function, and in all other respects the function remains a
member of the base class.

So my "... a bit like" is correct (only) as far as overload
resolution goes.

Rob. -- http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #7
Rob Williscroft wrote:
...
7.3.3/13: (Theusingdeclar ation)

For the purpose of overload resolution, the functions which are
introduced by a using-declaration into a derived class will be treated
as though they were members of the derived class. In particular, the
implicit this parameter shall be treated as if it were a pointer to the
derived class rather than to the base class. This has no effect on the
type of the function, and in all other respects the function remains a
member of the base class.
...


Yes, that explains the Comeau's behavior. The first sentence at least. I
find everything after the first one a bit misleading since it talks
about implict parameter as a _pointer_, while in overload resolution
sections the standard defines the implied object parameter to be a
_reference_.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
4956
by: Stephan Kurpjuweit | last post by:
Hi, could you please help me with the following example? struct Base { virtual Base& operator = (const Base &k) {} };
2
9781
by: Arvid Requate | last post by:
Hello, I'd like to understand why the following code does not compile. It looks like a strangeness in connection with overload resolution for the <complex> header: The conversion operator double() of class B is called for the member complex::operator*=(double) as expected, but not for operator*(complex, double). The effect is, that the template matching (or overload resolution)
19
2222
by: scroopy | last post by:
Is it impossible in C++ to create an assignment operator for classes with const data? I want to do something like this class MyClass { const int m_iValue; public: MyClass(int iVal):m_iValue(iVal){}
11
3810
by: jakester | last post by:
I am using Visual C++ 2007 to build the code below. I keep getting linkage error. Could someone please tell me what I am doing wrong? The code works until I start using namespace for my objects. Error 1 error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char & __cdecl graph::operator<<(class std::basic_ostream<char,struct std::char_traits<char &,class graph::Node &)" (??6graph@@YAAAV?...
14
2802
by: Jess | last post by:
Hi, I read about operator overloading and have a question regarding "operator->()". If I have two classes like this: struct A{ void f(); }; struct B{
8
2113
by: pauldepstein | last post by:
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}
17
1897
by: My interest | last post by:
How can I use * operator in C# generics? e.g. class foo<T_> { T_ v1, v2: public T_ squar() { return v1 * v2; /// wrong! }
9
2888
by: puzzlecracker | last post by:
From my understanding, if you declare any sort of constructors, (excluding copy ctor), the default will not be included by default. Is this correct? class Foo{ public: Foo(int); // no Foo() is included, i believe. };
5
3695
by: jknupp | last post by:
In the following program, if the call to bar does not specify the type as <int>, gcc gives the error "no matching function for call to ‘bar(A&, <unresolved overloaded function type>)’". Since bar explicitly takes a pointer-to-member with no parameters, why is the lookup for the overloaded function not able to use the number of arguments to determine the appropriate function? Is there a relevant portion of the standard that governs the...
0
8628
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8567
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9114
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
8981
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
8823
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
7660
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
5839
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4578
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
3
1973
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.