473,466 Members | 1,336 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

Interesting overload case

The following program illustrates an interesting effect of the way C++
resolves function overloading.

I have verified with a member of the C++ stardard committee that the output
shown is correct.

*********************************Program********** **********************

#include <iostream>
using std::cout;
using std::endl;

namespace test
{
enum test1 { testa=3, testb=4 };

std::ostream& operator<<(std::ostream& os, test::test1 t)
{
os << " **** TEST1 **** ";
return os;
}

enum test2 { testc = 5, testd = 7 };
}

std::ostream& operator<<(std::ostream& os, test::test2 t)
{
os << " **** TEST2 **** ";
return os;
}

test::test1 i(test::testa);
test::test2 j(test::testc);

namespace test
{
void testy()
{
cout << i << endl;
cout << j << endl;
}
}

int main()
{
cout << "Direct\n";
cout << i << endl;
cout << j << endl;
cout << "Indirect\n";
test::testy();
return 0;
}

********************************Output************ ********************

Direct
**** TEST1 ****
**** TEST2 ****
Indirect
**** TEST1 ****
5
Jul 22 '05 #1
8 1563

"David Sachs" <sa***@fnal.gov> wrote in message
news:2K********************@comcast.com...
The following program illustrates an interesting effect of the way C++
resolves function overloading.

I have verified with a member of the C++ stardard committee that the output shown is correct.

*********************************Program********** **********************

#include <iostream>
using std::cout;
using std::endl;

namespace test
{
enum test1 { testa=3, testb=4 };

std::ostream& operator<<(std::ostream& os, test::test1 t)
{
os << " **** TEST1 **** ";
return os;
}

enum test2 { testc = 5, testd = 7 };
}

std::ostream& operator<<(std::ostream& os, test::test2 t)
{
os << " **** TEST2 **** ";
return os;
}

test::test1 i(test::testa);
test::test2 j(test::testc);

namespace test
{
void testy()
{
cout << i << endl;
cout << j << endl;
}
}

int main()
{
cout << "Direct\n";
cout << i << endl;
cout << j << endl;
cout << "Indirect\n";
test::testy();
return 0;
}

********************************Output************ ********************

Direct
**** TEST1 ****
**** TEST2 ****
Indirect
**** TEST1 ****
5


What is the cause of this?
Jul 22 '05 #2
"Dave" <be***********@yahoo.com> wrote...

"David Sachs" <sa***@fnal.gov> wrote in message
news:2K********************@comcast.com...
The following program illustrates an interesting effect of the way C++
resolves function overloading.

I have verified with a member of the C++ stardard committee that the

output
shown is correct.

*********************************Program********** **********************

#include <iostream>
using std::cout;
using std::endl;

namespace test
{
enum test1 { testa=3, testb=4 };

std::ostream& operator<<(std::ostream& os, test::test1 t)
{
os << " **** TEST1 **** ";
return os;
}

enum test2 { testc = 5, testd = 7 };
}

std::ostream& operator<<(std::ostream& os, test::test2 t)
{
os << " **** TEST2 **** ";
return os;
}

test::test1 i(test::testa);
test::test2 j(test::testc);

namespace test
{
void testy()
{
cout << i << endl;
cout << j << endl;
}
}

int main()
{
cout << "Direct\n";
cout << i << endl;
cout << j << endl;
cout << "Indirect\n";
test::testy();
return 0;
}

********************************Output************ ********************

Direct
**** TEST1 ****
**** TEST2 ****
Indirect
**** TEST1 ****
5


What is the cause of this?


The cause is that when resolving which operator<< to use for
test::test2 _inside_ 'testy' function, the compiler is _not_
considering the global namespace, and cannot find the output
operator defined just above 'i' and 'j' objects. It has to
resort to finding a suitable std::ostream::operator<< along
with converting an enum value into 'int'.

At least that's how I remember it.

Victor
Jul 22 '05 #3
Hello NG,

just to make some more mess I changed a little bit the code here:

// **** CODE CHANGES ****
namespace test
{
void testy()
{
operator<<(cout,i) << endl;
cout << j << endl; // **** TEST2 **** in bcc and "5" in MSVC and g++
// operator<<(cout,j) << endl;
// this one will not compile with MSVC 7.1, g++3.3.1
// but compiles with bcc 5.5.1: **** TEST2 ****
::operator<<(cout,j) << endl; // **** TEST2 ****
// but this works how expected:
}
}

// **** CODE END ****

I used the direct call of the operator, and I think it is getting really
weird. Nicely enough the borland compiler does not agree with the others
(neither did it before the change). In fact I have promblems understanding
it :-(

1.) I my opinion the call

A << B;

should produce the same result like

operator<<(A,B);

If not, why and for what reason not? Is it still transparent then?

But:
MSVC7.1 and g++ will not compile

operator<<(cout,j) << endl; // neither: std::operator<<(cout,j) << endl;

but both compile this line:

cout << j << endl;

which operator is called then?

2.) This one compiles and works fine

::operator<<(cout,j) << endl;

does what it should do :-). Nice
Regards,
Patrick
Jul 22 '05 #4
"Patrick Kowalzick" <Pa***************@cern.ch> wrote in message
news:bq*********@sunnews.cern.ch...
Hello NG,

just to make some more mess I changed a little bit the code here:

// **** CODE CHANGES ****
namespace test
{
void testy()
{
operator<<(cout,i) << endl;
cout << j << endl; // **** TEST2 **** in bcc and "5" in MSVC and g++
// operator<<(cout,j) << endl;
// this one will not compile with MSVC 7.1, g++3.3.1
// but compiles with bcc 5.5.1: **** TEST2 ****
::operator<<(cout,j) << endl; // **** TEST2 ****
// but this works how expected:
}
}

// **** CODE END ****

I used the direct call of the operator, and I think it is getting really
weird. Nicely enough the borland compiler does not agree with the others
(neither did it before the change). In fact I have promblems understanding
it :-(

1.) I my opinion the call

A << B;

should produce the same result like

operator<<(A,B);

If not, why and for what reason not? Is it still transparent then?

But:
MSVC7.1 and g++ will not compile

operator<<(cout,j) << endl; // neither: std::operator<<(cout,j) << endl;

but both compile this line:

cout << j << endl;

which operator is called then?

2.) This one compiles and works fine

::operator<<(cout,j) << endl;

does what it should do :-). Nice
Regards,
Patrick

There actually is a subtle difference between

cout << j;

and

operator<<(cout,j);

when the enem-specific form of operator<< is unavailable.

The first form (cout << j) will cause integral promotion of j. The C++
standard explicitly states that the enum will be converted to an int if
feasible, This feature allows displaying the numeric value of an enum if
operator<< is not overloaded for it.

The second form (explicit function call) will use whatever conversions of
the enum are feasible. For some C++ compilers such as g++, this will cause a
compile time error, because the forms

operator<<(ostream&, char)
operator<<(ostream&, unsigned char)
operator<<(ostream&, signed char)

are equally good, and better than anything else. A compiler that stores enum
s as int s would not notice this problem.
Jul 22 '05 #5
Dave wrote:
The following program illustrates an interesting effect of the way C++
resolves function overloading.

I have verified with a member of the C++ stardard committee that the

output
shown is correct.

*********************************Program********** **********************

#include <iostream>
using std::cout;
using std::endl;

namespace test
{
enum test1 { testa=3, testb=4 };

std::ostream& operator<<(std::ostream& os, test::test1 t)
{
os << " **** TEST1 **** ";
return os;
}

enum test2 { testc = 5, testd = 7 };
}

std::ostream& operator<<(std::ostream& os, test::test2 t)
{
os << " **** TEST2 **** ";
return os;
}

test::test1 i(test::testa);
test::test2 j(test::testc);

namespace test
{
void testy()
{
cout << i << endl;
cout << j << endl;
}
}

int main()
{
cout << "Direct\n";
cout << i << endl;
cout << j << endl;
cout << "Indirect\n";
test::testy();
return 0;
}

********************************Output************ ********************

Direct
**** TEST1 ****
**** TEST2 ****
Indirect
**** TEST1 ****
5


What is the cause of this?
...


Inside 'test::testy' the name lookup for 'operator <<' works as follows:

1) Ordinary unqualified name lookup for 'operator<<' begins in namespace
'test' and immediately finds 'test::operator<<'.
2) Argument-dependent name lookup on the first argument finds a bunch of
'std::basic_ostream<>::operator<<' functions.
3) Argument-dependent name lookup on the second argument also finds
'test::operator<<'

Note that '::operator<<' is not found by name lookup. In this situation
overload resolution has no choice but to go with one of 'operator <<'
member functions of 'std::basic_ostream<>', since 'test::operator<<'
cannot be used with value of type 'test2'.

Inside 'main' the name lookup for 'operator <<' works as follows:

1) Ordinary unqualified name lookup for 'operator<<' begins in global
scope and finds '::operator<<'.
2) Argument-dependent name lookup on the first argument finds a bunch of
'std::basic_ostream<>::operator<<' functions.
3) Argument-dependent name lookup on the second argument finds
'test::operator<<'

In this case overload resolution will choose '::operator<<' as the best
match. That explains the behavior of the program.

Note, that if you remove the declaration of 'test::operator<<', the
ordinary unqualified name lookup inside 'test::testy' will not stop
inside namespace 'test' and will continue into the global scope, where
it will find '::operator<<'. In this case, both 'test::testy' and 'main'
will output identical results.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #6
Andrey Tarasevich wrote:
...
Inside 'test::testy' the name lookup for 'operator <<' works as follows:

1) Ordinary unqualified name lookup for 'operator<<' begins in namespace
'test' and immediately finds 'test::operator<<'.
2) Argument-dependent name lookup on the first argument finds a bunch of
'std::basic_ostream<>::operator<<' functions.
3) Argument-dependent name lookup on the second argument also finds
'test::operator<<'

Note that '::operator<<' is not found by name lookup. In this situation
overload resolution has no choice but to go with one of 'operator <<'
member functions of 'std::basic_ostream<>', since 'test::operator<<'
cannot be used with value of type 'test2'.

Inside 'main' the name lookup for 'operator <<' works as follows:

1) Ordinary unqualified name lookup for 'operator<<' begins in global
scope and finds '::operator<<'.
2) Argument-dependent name lookup on the first argument finds a bunch of
'std::basic_ostream<>::operator<<' functions.
3) Argument-dependent name lookup on the second argument finds
'test::operator<<'

In this case overload resolution will choose '::operator<<' as the best
match. That explains the behavior of the program.

Note, that if you remove the declaration of 'test::operator<<', the
ordinary unqualified name lookup inside 'test::testy' will not stop
inside namespace 'test' and will continue into the global scope, where
it will find '::operator<<'. In this case, both 'test::testy' and 'main'
will output identical results.


It is probably worth adding that in short this behavior is explained by
the simple fact that form the point of view of function 'test::testy'
'operator<<' declared inside namespace 'test' _hides_ global
'operator<<'. The global 'operator<<' can be "unhidden" by a
using-declaration in namespace 'test'

namespace test
{
using ::operator<<;

void testy()
{
cout << i << endl;
cout << j << endl;
}
}

In this case 'testy' will call the same 'operator <<' functions as
'main' does.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #7
Hello David,
There actually is a subtle difference between

cout << j;

and

operator<<(cout,j);

when the enem-specific form of operator<< is unavailable.

The first form (cout << j) will cause integral promotion of j. The C++
standard explicitly states that the enum will be converted to an int if
feasible, This feature allows displaying the numeric value of an enum if
operator<< is not overloaded for it.

The second form (explicit function call) will use whatever conversions of
the enum are feasible. For some C++ compilers such as g++, this will cause a compile time error, because the forms

operator<<(ostream&, char)
operator<<(ostream&, unsigned char)
operator<<(ostream&, signed char)

are equally good, and better than anything else. A compiler that stores enum s as int s would not notice this problem.


This I got and later this day I try to find it in the Standard. But I do not
like it. For me it seems not to be transparent if there is such a difference
between the first and the second form.

And doesnt't this mean, that your original program may produce

Direct
**** TEST1 ****
**** TEST2 ****
Indirect
**** TEST1 ****
5

what is obviously right, but may produce as well with another compiler

Direct
**** TEST1 ****
**** TEST2 ****
Indirect
**** TEST1 ****
**** TEST2 ****

what could be a correct result as well. It just dependes on the type used
for enum. This would explain the differnt behavior of g++ (and MSVC)
compared to bcc.
Is this correct like this?

Regard,
Patrick
Jul 22 '05 #8
Patrick Kowalzick wrote:

And doesnt't this mean, that your original program may produce

Direct
**** TEST1 ****
**** TEST2 ****
Indirect
**** TEST1 ****
5

what is obviously right, but may produce as well with another compiler

Direct
**** TEST1 ****
**** TEST2 ****
Indirect
**** TEST1 ****
**** TEST2 ****

what could be a correct result as well. It just dependes on the type used
for enum. This would explain the differnt behavior of g++ (and MSVC)
compared to bcc.
Is this correct like this?
...


No, it doesn't depend on the type used for enum. The behavior of the
original program is explained by C++ name lookup rules. See my messages
in this thread for complete explanation.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #9

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

Similar topics

4
by: Chiller | last post by:
Ok, thanks to some good assistance/advice from people in this group I've been able to further develop my Distance class. Since previous posts I've refined my code to accept the unit measurement...
9
by: Mario Charest | last post by:
.. Hi, It's possible to overload the operator, but I wonder if it's possible to somehow overload and so forth. My goal would be to switch from static array declaration into something...
0
by: Alex VanderWoude | last post by:
Okay, now I know how to directly reference a particular overload of a method in a cref. However, now I have the "opposite" problem. I want to directly reference the general summary page for all...
17
by: Chris | last post by:
To me, this seems rather redundant. The compiler requires that if you overload the == operator, you must also overload the != operator. All I do for the != operator is something like this: ...
2
by: Michi Henning | last post by:
Hi, the following code produces an error on the second-last line: Interface Left Sub op() End Interface Interface Right Sub op(ByVal i As Integer)
9
by: Tony | last post by:
I have an operator== overload that compares two items and returns a new class as the result of the comparison (instead of the normal bool) I then get an ambiguous operater compile error when I...
2
by: xtrigger303 | last post by:
Hi to all, I was reading Mr. Alexandrescu's mojo article and I've a hard time understanding the following. Let's suppose I have: //code struct A {}; struct B : A {};
5
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...
1
by: fabian.lim | last post by:
Hi all, Im having a problem with my code. Im programming a vector class, and am trying to overload the () operator in 2 different situations. The first situation is to assign values, e.g. Y =...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
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,...
0
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...
0
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...
1
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
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...
0
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,...
0
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?

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.