C++ reluctant to overload function

K

Keith H Duggar

Did you try this? Add #include <math.h> and it should work just fine.

Or, if you're a namespace purist, use #include <cmath>

Aren't the compatibility .h headers deprecated by the C++ standard?
(See Annex 0).
and put your function in namespace std like the rest of the math functions.

Since his specialization does not depend on any user defined
type, and also since sin is not a template anyhow (?) (and so
can't qualify for the template specialization exception below)
wouldn't your advice result in undefined behavior?

17.3.3.1 Reserved names
[lib.reserved.names]

1 It is undefined for a C++ program to add declarations or
definitions
to namespace std or namespaces within namespace std unless
otherwise
specified. A program may add template specializations for any
stan-
dard library template to namespace std. Such a specialization
(com-
plete or partial) of a standard library template results in
undefined
behaviour unless the declaration depends on a user-defined name
of
external linkage and unless the specialization meets the
standard
library requirements for the original template.20)

KHD
 
J

Johannes Schaub (litb)

Victor said:
I'd prefer to see "nitpick" to be attached to the first sentence in your
reply.

Whatever you want to call it, it still is a valid counter-example against
the statement that names in the same scope can't hide each other. One nit-
pick picks on another nit-pick. That's usenet.
 
V

Victor Bazarov

Whatever you want to call it, it still is a valid counter-example against
the statement that names in the same scope can't hide each other. One nit-
pick picks on another nit-pick. That's usenet.

Yours wasn't really a counter-example (nor was it a nit pick, so I take
my request back). See the highlighted portion of James' post above.
The cases you presented as "hiding" are already covered by him. So, not
generally "names can hide one another" but "a name of a class can
coexist with the name of another item" - the special provision James was
talking about.

V
 
J

Johannes Schaub (litb)

Victor said:
Yours wasn't really a counter-example (nor was it a nit pick, so I take
my request back). See the highlighted portion of James' post above.
The cases you presented as "hiding" are already covered by him. So, not
generally "names can hide one another" but "a name of a class can
coexist with the name of another item" - the special provision James was
talking about.

Coexistence does not nearly imply name hiding. Otherwise, overloaded
functions would hide each other.
 
J

Juha Nieminen

Victor Bazarov said:
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,

foo(int) is not the same thing as foo(int,int). They are two different
functions with different signatures. In the original post one is hiding
the other. I can't understand the rationale for that.
 
J

Juha Nieminen

James Kanze said:
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.

A 'foo(int)' in an inner scope hiding a 'foo(int)' in the outer
scope is understandable.

However, a 'foo(int)' in the inner scope hiding a 'foo(int,int)'
in the outer scope isn't. I don't understand why it has to be hidden,
which is why I asked for the rationale for that. What would be the
problem with an unqualified call like "foo(1, 2)" calling the function
in the outer scope? It's not like it can be confused with the function
in the inner scope (because the amount of parameters is clearly
different).
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.

In this example the function call would match two function
declarations, in which case the innermost scoped declaration wins.
However, if the choices are 'foo(int)' and 'foo(int,int)', what
is the reason to make the inner declaration hide the outer one?
 
V

Victor Bazarov

foo(int) is not the same thing as foo(int,int). They are two different
functions with different signatures. In the original post one is hiding
the other. I can't understand the rationale for that.

Overloading is based on the *type* of the function, not the name,
although it does require the name to be the same. Another major
requirement for overloading is that both *names* have to be declared in
the same *scope*. In the original post the functions are declared in
different scopes, and overloading does NOT apply, and hiding does. Such
is the design of the language. What is it exactly that you don't
understand? The rationale for what?

V
 
Ö

Öö Tiib

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.  

I do not really see why you need them as overloads but hopefully you
tell the reason.

Lets first see what we have. There are several sin() in current C++.
1) In <complex> there is such std::sin():

namespace std
{
template<class T> complex<T> sin(const complex<T>& x);
}

2) In <valarray> there is such std::sin():

namespace std
{
template<class T> valarray<T> sin(const valarray<T>&);
}

3) In <cmath> there are such sin():

double sin(double);
float sin(float);
long double sin(long double);

Lets now see solutions.

1) These are all usable side-by-side. Just call ::sin() for double,
float and long double or std::sin() for valarray<T> and complex<T>.

2) If you want to define your own sin in your scope (namespace) and
have the C++ sin variants also there as overloads:

#include <complex>
#include <valarray>
#include <cmath>

namespace stevemath
{
using ::sin;
using std::sin;
vector<double> sin(vector<double> x);
}

3) If you want them to be all useful as overloads in a global
namespace then that is bad idea to burden global namespace but also
doable:

#include <complex>
#include <valarray>
#include <cmath>
using std::sin;
vector<double> sin(vector<double> x); // Steves sin

So what is so difficult about it?
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.

I do not understand ... why you need them all sins as overloads of
each other. If there is reason then i don not understand how can it be
so good i have not met it but you desire it without indicating it?

Majority would really hate if some of thousands of functions from
<windows.h> or other place like that started to compete in overload
resolution with member functions within their classes or with
functions within their namespace and cause typos to compile for
confusing results. Describe the poor design choice you are steered
towards. For me C++ is as liberal as it can only get when choices of
paradigms, idioms or design patterns are under discussion.
 
V

Victor Bazarov

A 'foo(int)' in an inner scope hiding a 'foo(int)' in the outer
scope is understandable.

However, a 'foo(int)' in the inner scope hiding a 'foo(int,int)'
in the outer scope isn't. I don't understand why it has to be hidden,
which is why I asked for the rationale for that.

The *rule* is that the *name* is looked up first, without the arguments.
That's how name lookup works. That's why the *name* alone hides
another name that is spelled the same way, regardless of *what the name
is of*.
> What would be the
problem with an unqualified call like "foo(1, 2)" calling the function
in the outer scope? It's not like it can be confused with the function
in the inner scope (because the amount of parameters is clearly
different).

So, what's important, the number of arguments or the number and the
In this example the function call would match two function
declarations, in which case the innermost scoped declaration wins.
However, if the choices are 'foo(int)' and 'foo(int,int)', what
is the reason to make the inner declaration hide the outer one?

The name lookup is already quite complicated. Mix in the arguments (the
number, but not the types), and you get plenty of problems that are
likely impossible to solve in a working compiler for all people to be
satisfied, I would guess.

It's easier for the language to be as strict as possible, but not
stricter. Making rules relaxed creates more problems than it solves.

V
 
J

James Kanze

Did you try this? Add #include <math.h> and it should work just fine.
Or, if you're a namespace purist, use #include <cmath> and put your
function in namespace std like the rest of the math functions.

Technically, that's not legal. But there's nothing to prevent
you from using a few "using" declarations, to bring names into
whatever scope you want.
 
Ö

Öö Tiib

3) In <cmath> there are such sin():

 double sin(double);
 float sin(float);
 long double sin(long double);

These are actually also put to std namespace, the global versions did
come from <math.h> that <cmath> includes.
 
J

James Kanze

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

Not so much arguing, but going into even more detail that I did
(which doesn't happen very often---I tend to be far too
detailed).
struct A { };
void A();
Name of function hides the name of struct.

That's the case I mentionned in the parentheses. It's one
I prefer to ignore, but it is certainly part of the language.
struct A {
int A;
};
Name of member hides name of struct (injected class name).

Hmmm. I would have thought that that was illegal.

In fact, it's really a special case of the above; you can still
access the struct with "struct A", and it is done for reasons of
C compatibility.
 
J

James Kanze

A 'foo(int)' in an inner scope hiding a 'foo(int)' in the
outer scope is understandable.
However, a 'foo(int)' in the inner scope hiding a 'foo(int,int)'
in the outer scope isn't.

Why not? How is this any different from an int foo hiding
a double foo?
I don't understand why it has to be hidden,

It doesn't have to. You could drop hiding completely, and make
all names visible everywhere. Experience has shown that this
isn't a particularly good idea.
which is why I asked for the rationale for that. What would be
the problem with an unqualified call like "foo(1, 2)" calling
the function in the outer scope? It's not like it can be
confused with the function in the inner scope (because the
amount of parameters is clearly different).

Because it would be inconsistent with name lookup for anything
else. And because it would make code extremely fragile; you'd
have to ensure that all functions had an exact match for all
arguments if you didn't want your code to break later.
In this example the function call would match two function
declarations, in which case the innermost scoped declaration
wins. However, if the choices are 'foo(int)' and
'foo(int,int)', what is the reason to make the inner
declaration hide the outer one?

Because it would be too confusing if name lookup took into
account the number and types of the arguments. You'd have to do
a very complex analysis just to know where to begin looking for
the name.
 
S

Steve Pope

Pete Becker said:
On 2010-10-13 11:59:29 -0400, Steve Pope said:

Did you try this? Add #include <math.h> and it should work just fine.
Or, if you're a namespace purist, use #include <cmath> and put your
function in namespace std like the rest of the math functions.
Either way, there's no "workaround" needed. Just follow the langauge rules.

In my view these are workarounds. YMMV.

Steve
 
S

Steve Pope

Öö Tiib said:
I do not really see why you need them as overloads but hopefully you
tell the reason.

Lets first see what we have. There are several sin() in current C++.
1) In <complex> there is such std::sin():

namespace std
{
template<class T> complex<T> sin(const complex<T>& x);
}

2) In <valarray> there is such std::sin():

namespace std
{
template<class T> valarray<T> sin(const valarray<T>&);
}

3) In <cmath> there are such sin():

double sin(double);
float sin(float);
long double sin(long double);

Lets now see solutions.

1) These are all usable side-by-side. Just call ::sin() for double,
float and long double or std::sin() for valarray<T> and complex<T>.

2) If you want to define your own sin in your scope (namespace) and
have the C++ sin variants also there as overloads:

#include <complex>
#include <valarray>
#include <cmath>

namespace stevemath
{
using ::sin;
using std::sin;
vector<double> sin(vector<double> x);
}

3) If you want them to be all useful as overloads in a global
namespace then that is bad idea to burden global namespace but also
doable:

#include <complex>
#include <valarray>
#include <cmath>
using std::sin;
vector<double> sin(vector<double> x); // Steves sin

So what is so difficult about it?

I did not say it was difficult.
I do not understand ... why you need them all sins as overloads of
each other.

For the same reason you'd ever want to overload by argument type...
producing more readable code.
Majority would really hate if some of thousands of functions from
<windows.h> or other place like that started to compete in overload
resolution with member functions within their classes or with
functions within their namespace and cause typos to compile for
confusing results.

Overloading is not always the most readable/useful way to do things,
but it is sometimes.

An example of a technique being undesireable does not mean that technique
is always useless.

Steve
 
S

Steve Pope

Pete Becker said:
On 2010-10-13 11:59:29 -0400, Steve Pope said:

Did you try this? Add #include <math.h> and it should work just fine.

Or, if you're a namespace purist, use #include <cmath> and put your
function in namespace std like the rest of the math functions.

Either way, there's no "workaround" needed. Just follow the langauge rules.

I notice that with #include <cmath>, in this sort of case g++ requires
neither a "using" statement, nor putting your overloaded function into
std... even when compiled with -pedantic.

This almost seems like a bug.

Steve
 
Ö

Öö Tiib

For the same reason you'd ever want to overload by argument type...
producing more readable code.

Yes, but it is only more readable when the function does same thing to
different arguments. The very same word in different contexts may have
different meaning and functions may do different things. With sin it
is perhaps same operation (unless it is about fall of man in other
context). Usage of overloads that do different operations (or only
slightly overlapping operations) are confusing, not more readable.
Overloading is not always the most readable/useful way to do things,
but it is sometimes.

An example of a technique being undesireable does not mean that technique
is always useless.

In context of some smaller scope a function name may mean different
operation than in enclosing scope. On case of Windows API there are
perhaps precious few who even know most of the names that it declares.
Language hides the names of enclosing scopes if you define them in
scope. How should a good language (that is not "steering developers to
a bad design choice") remove confusing function names of outer scope
from overload resolution?
 
S

Steve Pope

Öö Tiib said:
In context of some smaller scope a function name may mean different
operation than in enclosing scope. On case of Windows API there are
perhaps precious few who even know most of the names that it declares.
Language hides the names of enclosing scopes if you define them in
scope. How should a good language (that is not "steering developers to
a bad design choice") remove confusing function names of outer scope
from overload resolution?

Offhand, I would say if a compiler for this hypothetical language can find
a unique function meeting the prototype over all visible scopes, then
it should use that function.

But if there are two functions with the same prototype (e.g. two
foo(int, int)'s) in two different scopes, and neither hides the other,
then it should bail.

Whether this can be done with a C++ style linker, as opposed to
Ada style with more passes throught the nametables, I'm not certain.


Steve
 
S

Steve Pope

Pete Becker said:
On 2010-10-13 18:37:35 -0400, Steve Pope said:
Don't get hung up on the linker. It has nothing to do with name lookup.

Here's a simple example of the danger in the approach you're
suggesting. Suppose you're using a library and it defines a base class
named Base. You create a derived class:

struct Derived: Base
{
void f(long);
};

and somewhere you call this function:

Derived d;
d.f(3); // calls Derived::f

Now you get a maintenance update for this third-part library and your
program stops working. Guess what: the developer added a function to
Base, void Base::f(int), and your code now calls that function instead
of Derived::f.

Interesting scenario.

Steve
 
N

Niklas Holsti

Pete said:
Don't get hung up on the linker. It has nothing to do with name lookup.

Here's a simple example of the danger in the approach you're suggesting.
Suppose you're using a library and it defines a base class named Base.
You create a derived class:

struct Derived: Base
{
void f(long);
};

and somewhere you call this function:

Derived d;
d.f(3); // calls Derived::f

Now you get a maintenance update for this third-part library and your
program stops working. Guess what: the developer added a function to
Base, void Base::f(int), and your code now calls that function instead
of Derived::f.

Since this thread has already mentioned Ada as an example of a language
where this kind of non-hiding can happen, perhaps it is apropos to
explain what would happen in Ada for this example:

Adding the function Base::f would make the call d.f(3) ambiguous, since
the compiler could not decide if the literal "3" should be taken as
integer or long-integer. Thus the compilation would fail, and the
programmer would have to say explicitly which type should be used for
"3", and this would determine if the call is to Base::f or to Derived::f.

So this example would "work" (be safe) in Ada because Ada does not have
the wide range of automatic type conversions and the "best match"
ambiguity resolver that C++ uses.

(I'm a habitual Ada programmer who is now trying to learn more about
C++. From my perspective, the automatic type conversions in C++ are
rather hair-raising :)
 

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,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top