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

Interesting overload case

P: n/a
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
Share this Question
Share on Google+
8 Replies


P: n/a

"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

P: n/a
"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

P: n/a
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

P: n/a
"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

P: n/a
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

P: n/a
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

P: n/a
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

P: n/a
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 discussion thread is closed

Replies have been disabled for this discussion.