C++ reluctant to overload function

S

Steve Pope

A very simple question follows.

Is it correct operation that the following fails to compile if
I uncomment the line indicated, and if so, what rule precludes
overloading the function foo() in the manner I am attempting
(based on type/number of operators)?

It seems unambiguous to me.

Thanks

Steve

*************************************


#include <cstdio>

struct a {

// uncommenting the following line causes it to fail to compile
// void foo(int x) { std::printf("%d\n", 2*x); }

void do_foo();
};

namespace {
void foo(int x, int y) {std::printf("%d\n", x + y); }
}

void a::do_foo() {
foo(2,4);
}

main() {
a y;
y.do_foo();
}
 
V

Victor Bazarov

A very simple question follows.

Is it correct operation that the following fails to compile if
I uncomment the line indicated, and if so, what rule precludes
overloading the function foo() in the manner I am attempting
(based on type/number of operators)?

It seems unambiguous to me.

Thanks

Steve

*************************************


#include<cstdio>

struct a {

// uncommenting the following line causes it to fail to compile
// void foo(int x) { std::printf("%d\n", 2*x); }

void do_foo();
};

namespace {
void foo(int x, int y) {std::printf("%d\n", x + y); }
}

void a::do_foo() {
foo(2,4);
}

main() {

int main() { // there is no implicit int in C++
a y;
y.do_foo();
}

The program with uncommented 'foo' member should fail to compile - too
many arguments for a::foo. Name lookup when in a member function finds
the member 'foo' which actually *hides* any other 'foo', and that's why
the '<unnamed namespace>::foo' should NOT be found during that lookup
and is *not* considered an overload.

What compiler finds the non-member 'foo' if you uncomment 'a::foo'?

V
 
S

Steve Pope

Pete Becker said:
On 2010-10-12 10:14:20 -0400, Steve Pope said:
Overloading only applies to names that are defined in the same scope.
Here, the first version of foo is defined as a member of struct a and
the second version of foo is defined in the anonymous namespace. Two
different scopes, so no overloading.

That does appear to be going on. Thanks.

Not sure it's a reasonable restriction though. Not that this
opinion counts for anything ;-)

Steve
 
S

Steve Pope

Victor Bazarov said:
On 10/12/2010 10:14 AM, Steve Pope wrote:

int main() { // there is no implicit int in C++
Thanks

The program with uncommented 'foo' member should fail to compile - too
many arguments for a::foo. Name lookup when in a member function finds
the member 'foo' which actually *hides* any other 'foo', and that's why
the '<unnamed namespace>::foo' should NOT be found during that lookup
and is *not* considered an overload.
What compiler finds the non-member 'foo' if you uncomment 'a::foo'?

None. It just seemed to me that, given the wild abandon with
which the linker will usually search for a function that matches
the prototype, I'm a bit surprised it doesn't find and use this one.


Steve
 
J

Juha Nieminen

Victor Bazarov said:
Name lookup when in a member function finds
the member 'foo' which actually *hides* any other 'foo', and that's why
the '<unnamed namespace>::foo' should NOT be found during that lookup
and is *not* considered an overload.

What is the rationale for that? I can't immediately think of a reason
or notivation for that.
 
S

Steve Pope

Victor Bazarov said:
On 10/12/2010 12:37 PM, Juha Nieminen wrote:
Rationale for what, exactly? For the rules of hiding? Names defined in
a closer scope hide names defined in the outer scope. It's an old rule,
that AFAICT existed from the start in C++. For example,

int main()
{
double d = 3.14159;
{
int d = 42;
{
d = 666; // changes the int d, not the double d
}
}

// here 'd' is still 3.14159
}

Well it's not exactly analogous. In the above case the name-mangled
names clash, in the case I described in my initial post, they do not.

Steve
 
S

Steve Pope

Victor Bazarov said:
On 10/12/2010 3:09 PM, Steve Pope wrote:
Not sure I understand what you mean here. In your example the name is
'foo'. In my example the name is 'd'. Would it make a difference if I
named my variables 'foo'? What's "name-mangled" have to do with name
resolution or with name hiding? And what *is* "name-mangled", anyway?

Unless I am missing something:

Overloaded function names do not hide each other because their
name-mangled names include the argument types.

There is no such distinctoin in your variable "d" in your example.

It seems in the situation in my first post, something is at work other
than a simple need to resolve names. Some matter of language design
philosophy that is more subtle than the scope of the names.


Steve
 
Ö

Öö Tiib

Unless I am missing something:

Overloaded function names do not hide each other because their
name-mangled names include the argument types.

Yes you miss. It is not linker who does resolve names. It is in
another way around. Linker is stupid. Compiler is smart. Nothing of it
is specified by language rules, it is just done that way by most
implementations.
There is no such distinctoin in your variable "d" in your example.

The d-s of Victor are of different type and if these had external
linkage then compiler had to name-mangle them differently to make
difference for linker.
It seems in the situation in my first post, something is at work other
than a simple need to resolve names.  Some matter of language design
philosophy that is more subtle than the scope of the names.

No. The C++ overload resolving and two-pass argument dependent lookup
and template argument deduction is as complex as it can only get for a
poor compiler. If it had to mix all the names from all the scopes
available without hiding the names in outer scope then that would
cause huge amount of ambiguities (compiler just fails to resolve) in
already existing code.
 
S

Steve Pope

Victor Bazarov said:
When *hiding* takes place, overloading is not present. That's what I
think you're missing. You can only overload functions in the same
scope. As soon as there is the same name declared in a more enclosed
scope, it *hides* the name from the outer scope. No overloading in that
case. None. Zero. Zilch. Nada.

Actually, poking into this I find that one can overload functions
across scopes, it just must be done explicitly. So I can fix
the example I first gave as follows.

Steve

*****************************


#include <cstdio>

struct a {

// uncommenting the following line no longer
// causes it to fail to compile

void foo(int x) { std::printf("%d\n", 2*x); }

void do_foo();
};

namespace {
void foo(int x, int y) {std::printf("%d\n", x + y); }
}

void a::do_foo() {
using ::foo; // because of this line
foo(2,4);
}

main() {
a y;
y.do_foo();
}
 
S

Stuart Redmann

[snipped previous discussion about issue with overloading]

The C++ overload resolving and two-pass argument dependent lookup
and template argument deduction is as complex as it can only get for a
poor compiler.

I agree that overload resolving and ADL are quite complex. However, I
don't see why C++ should be a poor language in that regard. Or do you
mean that most compilers are quite bad at error messages when it comes
to errors with ADL/overload resolution? I'd value your opinion.

Regards,
Stuart
 
V

Vladimir Jovic

Stuart said:
[snipped previous discussion about issue with overloading]

The C++ overload resolving and two-pass argument dependent lookup
and template argument deduction is as complex as it can only get for a
poor compiler.

I agree that overload resolving and ADL are quite complex. However, I
don't see why C++ should be a poor language in that regard. Or do you
mean that most compilers are quite bad at error messages when it comes
to errors with ADL/overload resolution? I'd value your opinion.

He probably meant that the poor compiler has to do all dirty and hard
work ;)

Most likely he didn't mean the c++ compilers or c++ language are crap.
 
J

James Kanze

What is the rationale for that? I can't immediately think of
a reason or notivation for that.

I suspect that the main reason is that that is how name lookup
always works. In just about every language which recognizes
scope. You wouldn't want to get a duplicate definition error
because you had defined an "int foo" at namespace scope, and
having the compiler find a function name, but not the name of
a variable, would be extremely confusing.

Beyond that, of course, you really don't want the compiler
looking further than you expect. Consider for a moment:

class C
{
void f(int);
public:
void g() { f('a'); }
};

When you wrote the class, it's obvious (I suppose) that you
intended g to call C::f. And you would be most annoyed if it
called some ::f some of the time (depending on what headers were
included before this header), but not others. Similarly, if
C derived from some other class, you wouldn't like the fact that
the author of that class added a private f(char) broke your
code.
 
J

James Kanze

Unless I am missing something:
Overloaded function names do not hide each other because their
name-mangled names include the argument types.

Overloaded function names do not hide each other because they
are in the same scope. Names in the same scope never hide one
another. A name in a given scope hides names in the enclosing
scope. The reason you can overload functions (but not
variables) is because the C++ standard allows more than one
symbol to be declared in any given scope, provided all of the
symbols are functions or function templates. (There's also
a special provision which allows the name of a class to be
declared when there is another declaration of the same name.
This is, however, a hack for C compatibility purposes, and is
best ignored.)

And there's no such thing as name mangling in C++. A variable
or a function has a name, and that's it.
There is no such distinctoin in your variable "d" in your
example.
It seems in the situation in my first post, something is at
work other than a simple need to resolve names. Some matter
of language design philosophy that is more subtle than the
scope of the names.

No. It's only a question of the scope of names.
 
Ö

Öö Tiib

[snipped previous discussion about issue with overloading]

The C++ overload resolving and two-pass argument dependent lookup
and template argument deduction is as complex as it can only get for a
poor compiler.

I agree that overload resolving and ADL are quite complex. However, I
don't see why C++ should be a poor language in that regard. Or do you
mean that most compilers are quite bad at error messages when it comes
to errors with ADL/overload resolution? I'd value your opinion.

It was said in context of OP expectation that names should not be
hidden.

What i meant that there are already things about that name resolving
in standard that are unclear. It is apparently not only me because
different compilers (for example ICC, g++ and MSVC) also handle some
edge cases differently and it is hard to tell who is right.
Portability issues about name resolving are usually easy to repair by
qualifying the names or by renaming or by using "using" directive only
that it is sometimes hard to understand that it is again such an
issue, when templates are involved.

Example given by Victor is on one hand a good example of name hiding
feature what language allows. On other hand it is hard to see who
really needs to hide a name like that (and why). It is very hard to
believe that writer of the function did really run out of possible
names.

Several would be happy if compiler gave warning on cases when a name
hides name in a bigger scope. For example when a name in derived class
hides a name in base class. I only remember cases of major confusion
and bugs with it. OP was similarly confused when name in nameless
namespace was hidden by name in class. Unfortunately a warning about
such hiding may initially cause too numerous warnings when including
some header-only libraries ... together with some large C API
(<windows.h> for example).
 
M

Marc

James said:
Beyond that, of course, you really don't want the compiler
looking further than you expect. Consider for a moment:

class C
{
void f(int);
public:
void g() { f('a'); }
};

When you wrote the class, it's obvious (I suppose) that you
intended g to call C::f. And you would be most annoyed if it
called some ::f some of the time (depending on what headers were
included before this header), but not others. Similarly, if
C derived from some other class, you wouldn't like the fact that
the author of that class added a private f(char) broke your
code.

From what I understood of the previous post, he may not have been asking
for an overload, but more like a fallback (2-phase type). That is, here
it would consider f OK and not look any further. Only when it would
otherwise give an error would it look further. That would probably be
too confusing, but at least it wouldn't break any code.
 
S

Steve Pope

Victor Bazarov said:
On 10/12/2010 10:53 PM, Steve Pope wrote:
But it is *not* overloading "across scopes". You're simply bringing all
names into *the same scope*. Only then they become overloaded. If
different scopes are involved, there can be no overloading, there is,
however, name hiding. There is no way to marry those two actions, it's
one or the other. If you redeclare the hidden name in the inner scope
(so it "comes out of hiding"), then hiding doesn't apply any longer, and
you can talk about overloading again.
Please get your terminology straight.

I'm happy with my terminology, thanks.

Steve
 
S

Steve Pope

Vladimir Jovic said:
Stuart Redmann wrote:
He probably meant that the poor compiler has to do all dirty and hard
work ;)

That is also how I interpreted it
Most likely he didn't mean the c++ compilers or c++ language are crap.

Maintaining the separate compilation model while keeping the naming/
scoping/overloading scheme general is a balancing act.

Steve
 
J

Johannes Schaub (litb)

James said:
Overloaded function names do not hide each other because they
are in the same scope. Names in the same scope never hide one
another. A name in a given scope hides names in the enclosing
scope. The reason you can overload functions (but not
variables) is because the C++ standard allows more than one
symbol to be declared in any given scope, provided all of the
symbols are functions or function templates. (There's also
a special provision which allows the name of a class to be
declared when there is another declaration of the same name.
This is, however, a hack for C compatibility purposes, and is
best ignored.)

Since I too like to argue about things - names in the same scope can
actually hide one another.

struct A { };
void A();

Name of function hides the name of struct.

struct A {
int A;
};

Name of member hides name of struct (injected class name).
 
S

Steve Pope

It was said in context of OP expectation that names should not be
hidden.

Correct. Not that I expect the language to behave differently
than it is designed, just that it was not clear to me why this was a
good way to design it.
What i meant that there are already things about that name resolving
in standard that are unclear. It is apparently not only me because
different compilers (for example ICC, g++ and MSVC) also handle some
edge cases differently and it is hard to tell who is right.
Portability issues about name resolving are usually easy to repair by
qualifying the names or by renaming or by using "using" directive only
that it is sometimes hard to understand that it is again such an
issue, when templates are involved.

Example given by Victor is on one hand a good example of name hiding
feature what language allows. On other hand it is hard to see who
really needs to hide a name like that (and why). It is very hard to
believe that writer of the function did really run out of possible
names.

The whole idea of overloading is to provide the programmer with
a form of polymorphism. e.g. I have a math library function

double sin(double x)

And I want to overloaded it with a vector version

vector<double> sin(vector<double> x)

Because one scope encloses the other I cannot do this without some
sort of workaround. If the nuisance factor of such workarounds inclines
the programmer to not use polymorphism when they should, then
in some sense the language design is steering them to a poor design choice.

Steve
 
V

Victor Bazarov

Since I too like to argue about things - names in the same scope can
actually hide one another.

struct A { };
void A();

Name of function hides the name of struct.

struct A {
int A;
};

Name of member hides name of struct (injected class name).

I'd prefer to see "nitpick" to be attached to the first sentence in your
reply. Only names of classes can be hidden in the same scope. Try

struct A {
typedef int A; // not OK - two types with the same name
};

A variable can hide a class, a function can hide a class - there is a
way to force the recognition of the name as that of the class by using
the elaborate type specifier. Templates are probably the close second
since the presence of < or the keyword 'template' can bias the
compiler's interpretation (can't recall all cases right now).

But that's about it. Variables can't share names, nor can they exist
alongside functions with the same name.

Besides, we did start talking about function names, not types (see the
original post).

V
 

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

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top