Topic: Overloading based on return type not allowed - why?
Author: Steve Clamage <stephen.clamage@Eng.Sun.COM>
Date: 1997/05/19 Raw View
Valentin Bonnard wrote:
>
> Oleg Zabluda <zabluda@math.psu.edu> writes:
> > There is only one use for an old-style
> > cast I can think of (which I've read in D&E): casting a derived
> > cass to a private base.
>
> It won't work: you can't do anything more with old-style casts
> than new style ones; old style cast are only shorter since
> they can do several things at the same time, not more powerfull.
No, Oleg is correct.
The last paragraph of 5.4 "Explicit type conversion (cast notation)"
lists several operations allowed with old-style casts, but which
cannot be performed by new-style casts. Casting to a private base
class is one of them. I think it is unfortunate that old-style
casts are still needed in some circumstances.
--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/05/19 Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:
> Never use old-style casts!
Right !
> There is only one use for an old-style
> cast I can think of (which I've read in D&E): casting a derived
> cass to a private base.
It won't work: you can't do anything more with old-style casts
than new style ones; old style cast are only shorter since
they can do several things at the same time, not more powerfull.
--
Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: jimX.hyslopX@leitchX.com (Jim Hyslop)
Date: 1997/05/20 Raw View
In article <337C4E89.6358@lonnds.ml.com>, pardoej@lonnds.ml.com
says...
<snip>
> Having to write (int) foo() and (double) foo() the whole time
> doesn't really buy us much.
Yeah, really - you might just as well declare your functions:
int int_foo();
double double_foo();
since you're going to have to type "int" and "double" anyway!
--
Jim Hyslop
Xjim.Xhyslop@leitchX.com (remove all X to email me)
Spam can go here: postmaster@cyberpromo.com postmaster@savetrees.com
First rule of laboratory work: hot glass looks the same as cool
glass.
Note to recruitment agencies: I am *NOT* interested in looking for a
new job. I will NOT pass your name on to other programmers because I
do not know you.
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/05/20 Raw View
Chris Waters <cwaters@systems.dhl.com> wrote:
: Now, I suppose we could add a new specialized cast just for this
: purpose: disambiguate_overloaded_function_cast<> or something, and
: then we'd have:
: cout << disambiguate_overloaded_function_cast<int>f();
You can do it now.
template<class T> T f();
f<int> { /* ... */ }; // syntax??
f<double> { /* ... */ }; // syntax??
cout << f<int>();
Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: Marcelo Cantos <marcelo@mds.rmit.edu.au>
Date: 1997/05/21 Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:
> Chris Waters <cwaters@systems.dhl.com> wrote:
> : Now, I suppose we could add a new specialized cast just for this
> : purpose: disambiguate_overloaded_function_cast<> or something, and
> : then we'd have:
>
> : cout << disambiguate_overloaded_function_cast<int>f();
>
> You can do it now.
>
> template<class T> T f();
> f<int> { /* ... */ }; // syntax??
> f<double> { /* ... */ }; // syntax??
Actually it's:
template <class T> T f();
int f<int>() { /* ... */ };
double f<double>() { /* ... */ };
cout << f<int>();
Furthermore, the above seems to imply (though that may not have been
Oleg's intention) that only specialisations may be explictly
disambiguated. This is not so, eg:
template <class T> T f();
cout << f<int>(); // legal even though f<int> not specialised and
// f<T>() not defined.
--
______________________________________________________________________
Marcelo Cantos, Research Assistant __/_ marcelo@mds.rmit.edu.au
Multimedia Database Systems Group, RMIT / _ Tel 61-3-9282-2497
723 Swanston St, Carlton VIC 3053 Aus/ralia ><_> Fax 61-3-9282-2490
Acknowledgements: errors - me; wisdom - God; funding - RMIT
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Ted Neward <tknewar@srv.pacbell.com>
Date: 1997/05/16 Raw View
I'm jumping into this in the middle of the discussion, so somebody
correct me if I've said something that's been said already.
> Videoman wrote:
> >
> > One of the things I've often struggled with, and have been confused
> > by, is the fact that C++ does not allow overloading based on return
> > types. Why is that? Here some some of my arguments for allowing it,
> > perhaps some of you could explain the rationale for disallowing it.
>
According to Bjarne Stroustrup, overloading based on return type was
disallowed due to the potentials for ambiguity, as is later pointed out
in this thread. It was his belief that allowing such a construct would
lead to the possibilities of the compiler "doing something the
programmer did not intend" by accident. (Not that we don't have that
problem already, but that's another issue.)
> The function overloading rules are amazingly complex and don't always
> produce the "expected" result even so. Programmers often have a
> difficult time determining which function will be called, and the
> problem is even worse for maintainers who didn't write the original
> code.
>
I'm not sure that I agree that the function overloading rules are
amazingly complex. I *will* agree, however, that the "expected" result
often isn't. Still, anecdotal evidence leads me to believe that this is
an issue of good design and interface coding, and not the fault of the
language.
> Adding overloading on return type would make the rules unbearably
> complex and further reduce the likelihood of people writing and
> reading code correctly.
>
Again, I'm not sure I agree. Where the call would be ambiguous to the
reader, the compiler would also complain, and require either an explicit
cast or some other workaround.
> It would also introduce many more opportunities for ambiguous code.
> Trivial example:
> int f();
> double f();
> cout << f();
> This example has to be ambiguous.
>
As somebody points out later in this thread, you change the cout call to
read:
cout << (int)f(); // or cout << (double) f();
// or int i = f(); cout << i;
> I think typical attempts to use overloading on return value are
> going to be ambiguous much of the time.
>
Perhaps. But I would also submit that there will also be occasions where
overloading on return value are also going to be quite useful.
> Any gains in utility or expressiveness are more than outweighed
> by tbe additional complexity in the language definition.
>
But we don't need to modify the language definition to obtain the
desired results. We can achieve the same results without having to
modify a single line of the C++ standard.
Consider one of the most useful examples of function overloading:
char* convert(unsigned int i);
char* convert(unsigned long l);
char* convert(double d);
char* convert(float f);
// and so forth
convert(), in this case, is a simple rewrite of the "x"to"x"() routines
from the Standard C Runtime library, to convert a number into its string
equivalent. Thus we can write:
cout << convert(i) << endl;
without having to worry about what i's type is within our code.
It would also be nice to be able to do the same from strings back to
numbers, no? But the C++ standard disallows it:
int convert(char*);
unsigned long convert(char*);
float convert(char*);
// and so forth
because we can't include the return type as part of the signature.
We *can*, however, do this:
class NumericResultProxy
{
public:
NumericResultProxy(char* str)
{ m_stringVersion = new char[strlen(str)+1]; strcpy(m_stringVersion,
str); }
~NumericResultProxy()
{ delete [] m_stringVersion; }
operator int()
{ return atoi(m_stringVersion); }
operator unsigned long()
{ return atol(m_stringVersion); }
operator float()
{ return atof(m_stringVersion); }
// and so forth, for each distinct type we care to convert to
};
NumericResultProxy convert(char* numStr)
{
return NumericResultProxy(numStr);
}
The trick here, of course, is that the unnamed temporary
NumericResultProxy object, when it is tried to be applied as a int or
long to a function or as an rvalue in an assignment statement, silently
invokes the appropriate user-defined conversion operator, and performs
the translation at that point. We get all the benefits of
overloads-by-return-type, without having to change a single line of the
C++ standard.
HTH....
Ted Neward
Technical Services Group
Pacific Bell, San Ramon, CA
I don't speak on behalf of anybody,
even myself. My wife won't let me.
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: hxie@gradin.cis.upenn.edu (Hong-liang Xie)
Date: 1997/05/16 Raw View
Can the following example be a case for overloading based on
return type (I posted the example a whlie back)? At least it
is useful for me. Right now I have to use an ugly workaround.
It is about virtual template functions ...
---------------------------------------------
class Base {
public:
...
template <class T>
virtual T* getData() = 0;
...
};
---------------------------------------------
template <class T>
class Derived : public Base {
T* data;
public:
...
T* getData() { return data; }
...
};
---------------------------------------------
Hong
CIS Dept
Univ of Pennsylvania
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/05/17 Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:
|> Videoman <videoman@tiac.net> wrote:
|> : Use cout << (double)f(); or cout << (int)f(); instead.
|>
|> and later:
|>
|> : return (T*)0;
|>
|> and many more old-style casts deleted.
|>
|> Never use old-style casts! There is only one use for an old-style
|> cast I can think of (which I've read in D&E): casting a derived
|> cass to a private base.
Another good reason is that my compiler doesn't support the new-style
casts yet. In practice, you cannot use the new style casts in portable
code. (What I actually do is use macros, which evaluate to the new
style casts when they are available, and the old style otherwise.)
--
James Kanze home: kanze@gabi-soft.fr +33 (0)1 39 55 85 62
office: kanze@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Steve Clamage <stephen.clamage@eng.sun.com>
Date: 1997/05/17 Raw View
Ted Neward wrote:
>
> > The function overloading rules are amazingly complex and don't always
> > produce the "expected" result even so. Programmers often have a
> > difficult time determining which function will be called, and the
> > problem is even worse for maintainers who didn't write the original
> > code.
> >
> I'm not sure that I agree that the function overloading rules are
> amazingly complex. I *will* agree, however, that the "expected" result
> often isn't. Still, anecdotal evidence leads me to believe that this is
> an issue of good design and interface coding, and not the fault of the
> language.
The draft standard takes a full 25 pages (chapter 13) to explain the
overloading rules. I call that amazingly complex. The complexity is
due in part to the desire to do what most programmers would expect
in most circumstances. The rules have been tweaked considerably over
time, and still do not always give what seems like a reasonable result.
Another large part of the complexity is due to interactions with other
parts of the language. For example, when you write "x+y", the
overloading
rules must take into account the built-in versions of + with possible
conversions from the types of x and y to built-in types, member
functions
(both static and non-static) of the class of x, stand-alone operator+
functions that are visible, plus functions defined in the same namespace
as the types of x and y that might not otherwise be considered.
You can mitigate the complexity in a program by writing only simple
overloads, but that doesn't mean the language rules are not complex.
Even so, just when you think you have a simple model, the rules can
trip you up. Example:
void g(int);
void g(double);
This works pretty well, until you find you have values of type "long",
for which neither function is preferred. Add "void g(long);" and you
still have ambiguity with unsigned values.
> > Adding overloading on return type would make the rules unbearably
> > complex and further reduce the likelihood of people writing and
> > reading code correctly.
> >
> Again, I'm not sure I agree. Where the call would be ambiguous to the
> reader, the compiler would also complain, and require either an explicit
> cast or some other workaround.
As in the above examples, often the programmer sees no ambiguity
but the compiler does. The rules for multiple arguments are byzantine
enough that it is hard for a programmer to decide whether there is
a single best match, and if so, which one. This forum has seen heated
discussions among experienced C++ programmers over such issues. Aguably,
you have a bad design when this is the case. Exactly my point: the rules
are complex.
> > It would also introduce many more opportunities for ambiguous code.
> > Trivial example:
> > int f();
> > double f();
> > cout << f();
> > This example has to be ambiguous.
> >
> As somebody points out later in this thread, you change the cout call to
> read:
>
> cout << (int)f(); // or cout << (double) f();
> // or int i = f(); cout << i;
You can always use explicit casts to force a particular overloaded
function to be called. But using casts is, or ought to be, a sign
that something is wrong with your program. Further, casts do poorly
as programs evolve. Continuing my function 'g' example:
g((long)u); // u is type unsigned int, cast avoids ambiguity
Now we add "void g(unsigned);" to the program. The above call still
compiles without warnings and calls g(long) -- but without the cast
would unambiguously call g(unsigned). Using casts is not a viable
answer.
--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: cwaters@systems.dhl.com (Chris Waters)
Date: 1997/05/17 Raw View
In article <337B4E3E.3D43@srv.pacbell.com>
Ted Neward <tknewar@srv.pacbell.com> writes:
>> It would also introduce many more opportunities for ambiguous code.
>> Trivial example:
>> int f();
>> double f();
>> cout << f();
>> This example has to be ambiguous.
>>
>As somebody points out later in this thread, you change the cout call to
>read:
> cout << (int)f(); // or cout << (double) f();
> // or int i = f(); cout << i;
The disadvantage to this is that this is a cast, and if f() isn't an
overloadable function (due to some change in the design or, or the
programmer mis-remembering which function is overloaded, or something),
this suddenly and silently becomes a *real* cast. And the worst type
of cast at that.
Now, I suppose we could add a new specialized cast just for this
purpose: disambiguate_overloaded_function_cast<> or something, and
then we'd have:
cout << disambiguate_overloaded_function_cast<int>f();
Determining how silly this is, and how likely it is to go into the
standard at this point is left as an exercise for the student.
Fortunately, as Ted went on to demonstrate, none of this is necessary,
as one can use a proxy class to obtain the same results already.
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Steve Clamage <stephen.clamage@eng.sun.com>
Date: 1997/05/17 Raw View
Hong-liang Xie wrote:
>
> Can the following example be a case for overloading based on
> return type (I posted the example a whlie back)? At least it
> is useful for me. Right now I have to use an ugly workaround.
> It is about virtual template functions ...
>
> ---------------------------------------------
> class Base {
> public:
> ...
> template <class T>
> virtual T* getData() = 0;
> ...
> };
> ---------------------------------------------
> template <class T>
> class Derived : public Base {
> T* data;
> public:
> ...
> T* getData() { return data; }
> ...
> };
> ---------------------------------------------
I guess I don't see your point. Member templates are not allowed
to be virtual functions, but it has nothing to do with
overloading on return type. It is because the compiler cannot
know how many virtual functions a template instance might have, and
new ones could be added without recompiling any existing code.
The implementation of virtual functions would thus need to become
excessively complex.
That said, just because someone can find a use for a proposed
language feature doesn't mean the feature should be added to
C++. D&E contains an extensive discussion on that point.
--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Julian Pardoe <pardoej@lonnds.ml.com>
Date: 1997/05/17 Raw View
Roger Bonzer wrote:
>
> Along the same lines, this is how addresses of overloaded functions
> are already dealt with.
>
> void foo(int);
> void foo(double);
> cout << (void *)&foo; // ambiguous, but....
> cout << (void *)(void (*)(int))&foo; // okay, grabs void foo(int)
> cout << (void *)(void (*)(double))&foo; // okay, grabs void foo(double)
>
> I'm not entirely convinced that overloading based on return types
> is a good idea, but C++ already supports it in certain cases.
Does it? I don't understand. In the above example the two foos are
distinguished by their argument types.
I think the problem is that if both return types and arguments
are overloaded resolving the overloading requires a tricky multi-
pass algorithm. The original Ada/Green rationale gave this
impression. This is fiddly to implement (makeing compiler bugs
likely) and hard for human readers to figure out. Given that
there is a lot of sentiment out there against overloading and
user-defined operators, I am not sure it is wise to make
the potential for confusion greater.
Having to write (int) foo() and (double) foo() the whole time
doesn't really buy us much.
-- jP --
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/05/10 Raw View
Steve Clamage <stephen.clamage@eng.sun.com> wrote:
: Adding overloading on return type would make the rules unbearably
: complex and further reduce the likelihood of people writing and
: reading code correctly.
: It would also introduce many more opportunities for ambiguous code.
: Trivial example:
: int f();
: double f();
: cout << f();
: This example has to be ambiguous.
As soon as my compiler begins supporting function templates well, I will
begin playing with the code like this:
template<class T> T get(istream& is){
T t;
is >> t;
return t;
}
I hope it will provide a much needed syntactic sugar for the
elimination of the uninitialized variables, whose values have
to be read from a stream:
const int i = get<int>(cout); // note that i is const, although it's
// value is read from a stream.
Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/05/11 Raw View
Videoman <videoman@tiac.net> writes:
> One of the things I've often struggled with, and have been confused
> by, is the fact that C++ does not allow overloading based on return
> types. Why is that?
Why don't C++ programmer want to say explicitly what they
want to do, at least sometimes ?
> Here some some of my arguments for allowing it,
> perhaps some of you could explain the rationale for disallowing it.
Simple: expression can be annalysed from sub-expression to
complete-expression (except with function pointer overloading).
Because of the implicit conversions, the rules would be too
complicated (some people already claim that they are
impossible to understand).
> The primary thing I could see, would be elimination for the need for
> conversion operators. It would also eliminate the senseless need for
> the user to mangle a function name.
>
> Current situation:
>
> extern BYTE ReadBYTE(void * address);
> extern WORD ReadWORD(void * address);
> WORD w = ReadWord(0xF000);
>
> Ideal situation:
>
> extern BYTE Read(void * address);
> extern WORD Read(void * address);
> WORD w = Read(0xF000); //automatically match closest return type
> BYTE b = Read(0x8000); //automatically match closest return type
Read (void*, WORD&);
Read (void*, BYTE&);
Read (0xF000, w);
Read (0xF000, b);
BTW, a cast from int to void* is needed here.
Note: you can search previous discution on this topic in csc
in DejaNews.
--
Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: videoman@tiac.net (Videoman)
Date: 1997/05/09 Raw View
One of the things I've often struggled with, and have been confused
by, is the fact that C++ does not allow overloading based on return
types. Why is that? Here some some of my arguments for allowing it,
perhaps some of you could explain the rationale for disallowing it.
The primary thing I could see, would be elimination for the need for
conversion operators. It would also eliminate the senseless need for
the user to mangle a function name.
Current situation:
extern BYTE ReadBYTE(void * address);
extern WORD ReadWORD(void * address);
WORD w = ReadWord(0xF000);
Ideal situation:
extern BYTE Read(void * address);
extern WORD Read(void * address);
WORD w = Read(0xF000); //automatically match closest return type
BYTE b = Read(0x8000); //automatically match closest return type
I see this as analogous in a way to both function overloading partial
ordering, as well as the explicit keyword.
It would also help eliminate extranous conversion operators in certain
circumstances, in which a single function is called which returns an
opaque type needing to be converted.
This could in fact be implemented easily by compiliers by transforming
an expression of
int y();
int x;
x = y()
into
int y() returncall; //special syntax, becomes void y(int & x);
int x;
y(x);
It might also be viable to declare both function call type explicitly,
or have the compilier synthesize the "returncall" style function, and
pass the return value by refence, which then gets assigned the
returned result of the original function.
Don't compiliers already do this to some extent, by converting
old-style object initializers under-the-hood to the new style?
CMyClass mc = 0; // really CMyClass mc(0); , right?
There are of course problems with passing by reference when the
original semantics were of pass-by-value, as well as a possible
problem with temporaries, if the compilier re-ordered the expression.
The ideal solution, then, would be to have compiliers implement type
signatures and overloading based on return type as well.
I'm sure someone will claim this will introduce ambiguities, etc., but
didn't function overloading do the same thing until function overload
partial ordering (I think that's it) was introduced?
Partially related:
I for one would like to see templates of this fashion become perfectly
valid:
template <class T>
T * GetNullPtr<T>()
{
return (T*)0;
}
// a specialization of the above, for T = String
String * GetNullPtr<String>()
{
static String nullString;
return &nullString;
}
template <int a, int b>
int calc()
{
return a+b;
}
// a specialization of the above, for a=1 and b=1
int calc<1,1>
{
return -1;
}
Unfortunately, none of these function examples will compile, due to
some error about the template parameter not used in the function
argument list. (Optima C++ 1.5 test drive edition for Win32)
The workaround thus far has been to create a class, with member
functions. Still, this seems like a kludge to me that should be fixed.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: Steve Clamage <stephen.clamage@eng.sun.com>
Date: 1997/05/10 Raw View
Videoman wrote:
>
> One of the things I've often struggled with, and have been confused
> by, is the fact that C++ does not allow overloading based on return
> types. Why is that? Here some some of my arguments for allowing it,
> perhaps some of you could explain the rationale for disallowing it.
The function overloading rules are amazingly complex and don't always
produce the "expected" result even so. Programmers often have a
difficult time determining which function will be called, and the
problem is even worse for maintainers who didn't write the original
code.
Adding overloading on return type would make the rules unbearably
complex and further reduce the likelihood of people writing and
reading code correctly.
It would also introduce many more opportunities for ambiguous code.
Trivial example:
int f();
double f();
cout << f();
This example has to be ambiguous.
I think typical attempts to use overloading on return value are
going to be ambiguous much of the time.
Any gains in utility or expressiveness are more than outweighed
by tbe additional complexity in the language definition.
--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: videoman@tiac.net (Videoman)
Date: 1997/05/12 Raw View
On 10 May 97 16:56:09 GMT, Oleg Zabluda <zabluda@math.psu.edu> wrote:
>As soon as my compiler begins supporting function templates well, I will
>begin playing with the code like this:
>
>template<class T> T get(istream& is){
> T t;
> is >> t;
> return t;
>}
Very interesting, as my compilier will not compile similar functions,
erroring about the template parameter(s) not present in the function's
argument list. Are there any that will, or will soon?
>I hope it will provide a much needed syntactic sugar for the
>elimination of the uninitialized variables, whose values have
>to be read from a stream:
>
>const int i = get<int>(cout); // note that i is const, although it's
> // value is read from a stream.
Ineresting. What purpose do you have in declaring i as const? If it is
initialized at runtime, then the compilier cannot use it to do CPE in
expressions. Perhaps you simply want to use it as a read-only
variable?
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/05/12 Raw View
Videoman <videoman@tiac.net> writes:
> On 10 May 97 16:56:09 GMT, Oleg Zabluda <zabluda@math.psu.edu> wrote:
> >I hope it will provide a much needed syntactic sugar for the
> >elimination of the uninitialized variables, whose values have
> >to be read from a stream:
> >
> >const int i = get<int>(cout); // note that i is const, although it's
> > // value is read from a stream.
>
> Ineresting. What purpose do you have in declaring i as const? If it is
> initialized at runtime, then the compilier cannot use it to do CPE in
> expressions. Perhaps you simply want to use it as a read-only
> variable?
It can keep i in register, which is impossible for a normal auto
if its address is taken (in C the register declaration keep
from taking the address). Of course, a compiler can optimise
less and not use const at all or more and keep more things in
register but this is non-trivial and few compiler can do
that (I think - I'd like to be wrong on this one).
Also some people here like functionnal programming, where there
is no variables, only constants.
--
Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: videoman@tiac.net (Videoman)
Date: 1997/05/12 Raw View
On 10 May 97 05:33:48 GMT, Steve Clamage <stephen.clamage@eng.sun.com>
wrote:
>Videoman wrote:
>>
>> One of the things I've often struggled with, and have been confused
>> by, is the fact that C++ does not allow overloading based on return
>> types. Why is that? Here some some of my arguments for allowing it,
>> perhaps some of you could explain the rationale for disallowing it.
>
>The function overloading rules are amazingly complex and don't always
>produce the "expected" result even so. Programmers often have a
>difficult time determining which function will be called, and the
>problem is even worse for maintainers who didn't write the original
>code.
I understand that maintenance of non-trival program code is of utmost
importance, but I also think that having a properly-defined language
that supports _all_ the ideoms a programmer would ever want (within a
regular, logical, and orthagonal framework) is even more important.
>Adding overloading on return type would make the rules unbearably
>complex and further reduce the likelihood of people writing and
>reading code correctly.
Unfortunately, I don't have much sympathy for programmers that cannot
handle the complexity of C++. Sure, I have some problems sometimes,
but I look them up and work them out. I would expect others to do
likewise. Should templates have been excluded, because they are too
"complex"? No, templates allow us to do so many more useful things.
>It would also introduce many more opportunities for ambiguous code.
>Trivial example:
> int f();
> double f();
> cout << f();
>This example has to be ambiguous.
So? Use cout << (double)f(); or cout << (int)f(); instead. That seems
an intuitive way to tell the compiler that I want to call f(), and I
want the result from f() to be a double. In this example, old-style
casts really aren't that bad. Perhaps it would be useful to add a
return_cast<...> style cast operator, if one dislikes the old-style
casts.
Indeed, look at this example for a way in which function overloading
by return type may actually remove the need for casts.
Old style:
long rand_gen();
long foo_l = rand_gen();
short foo_s = rand_gen(); //warning: loss of precision
New style: (calls function appropriate to return type)
template<class T>
T rand_gen();
long rand_gen<long>() {...}
short rand_gen<short>() {...}
long foo_l = rand_gen(); //calls rand_gen<long>()
short foo_s = rand_gen(); //calls rand_gen<short>()
short foo_s = (short)(long)rand_gen(); //calls rand_gen<long>(),
//casts to short
Or: (removes the need for explicit casts)
long rand_gen();
template<class T>
inline T rand_gen_wrapper()
{ return (T)rand_gen(); }
long foo_l = rand_gen_wrapper();
short foo_s = rand_gen_wrapper(); //loss of precision due to cast
//same effect as old style, but no casts needed in main source
Admittedly, the above was quickly contrived. However, I think it would
be incredibly useful for things like generating the proper type of
null ptr for any particular type. This construct would be extremely
useful in conditional expressions to check for null ptrs.
template<class T>
T* NULL()
{
return (T*)0;
}
footype * foo;
if (foo == NULL())
//throw, etc
//this might be interesting - what type is the expression? :)
//perhaps should use a default template parameter of void
if (NULL() == NULL())
Perhaps my views on this are because I have started to look at
programs in more of a dataflow fashion. I imagine that functions are
like "connectors" that take input(s) and give output(s).
For a physical analogy, consider a set of pipe of various diameters,
and a set of connectors that the pipes fit into. To me, it seems
better to use the appropriate pipe for the job, rather than use the
largest size that would ever be needed, and then use adaptors to adapt
that size to the one really needed.
If you start to look at things that way, you might agree upon the need
for overloading based upon return type.
>I think typical attempts to use overloading on return value are
>going to be ambiguous much of the time.
Perhaps, perhaps not. There was a large problem with template
ambiguity until partial specialization rules were ironed out. I expect
that the same will happen here. Indeed, it seems possible to use
"explicit" on a function declaration's return type such that that
function will only be called via overloading if the value being
assigned into matches the return type of the function.
explicit long foo();
int foo();
//unambigous, long int foo() will only be called if the value
//returned is used as a long.
int i = foo();
long l = foo();
Granted, that might preclude the use of the (void)value idiom to
silence compilier warnings about unused values.
(void)foo(); // will call int foo();
(void)foo(); //if int foo() was not declared above, long foo()
//could not be called because of explicit, so error.
(void)(long)foo();.//will call long foo();
//(long) is needed to call correct overload
//double-cast seems messy though
>Any gains in utility or expressiveness are more than outweighed
>by tbe additional complexity in the language definition.
I honestly don't think complexity is a reasonable argument against it.
Flexibility tends to add complexity, but allows even greater
usefulness and degrees of freedom within the system in which it is
implemented. I would rather be free and complex than simple and
constrained.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: rogerb@imagebuilder.com (Roger Bonzer)
Date: 1997/05/14 Raw View
In article <3376c2cc.11410079@news.tiac.net>, videoman@tiac.net wrote:
> >It would also introduce many more opportunities for ambiguous code.
> >Trivial example:
> > int f();
> > double f();
> > cout << f();
> >This example has to be ambiguous.
>
> So? Use cout << (double)f(); or cout << (int)f(); instead. That seems
> an intuitive way to tell the compiler that I want to call f(), and I
> want the result from f() to be a double. In this example, old-style
> casts really aren't that bad. Perhaps it would be useful to add a
> return_cast<...> style cast operator, if one dislikes the old-style
> casts.
>
Along the same lines, this is how addresses of overloaded functions
are already dealt with.
void foo(int);
void foo(double);
cout << (void *)&foo; // ambiguous, but....
cout << (void *)(void (*)(int))&foo; // okay, grabs void foo(int)
cout << (void *)(void (*)(double))&foo; // okay, grabs void foo(double)
I'm not entirely convinced that overloading based on return types
is a good idea, but C++ already supports it in certain cases.
rogerb
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/05/14 Raw View
Videoman <videoman@tiac.net> wrote:
: Use cout << (double)f(); or cout << (int)f(); instead.
and later:
: return (T*)0;
and many more old-style casts deleted.
Never use old-style casts! There is only one use for an old-style
cast I can think of (which I've read in D&E): casting a derived
cass to a private base.
In your post, all old-style casts should be replaced with static_cast<>.
Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]