Topic: explicit' argument extension?


Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 25 Jan 1995 17:55:45 GMT
Raw View
In article MIn@world.std.com, tob@world.std.com (Tom O Breton) writes:
>clamage@Eng.Sun.COM (Steve Clamage) writes:
>> There was a LONG thread on [the explicit keyword extended to argument
>> lists] a little while ago.
>>
>> IMHO it's a bad idea because the decision about what conversions
>> should be allowed does not belong to the function being called,
>> but to the caller.
>
>I agree with the principle, but IMO it is already violated, with
>overloaded functions. Overloaded functions already control what
>conversions are legal

No they don't.

>and which functions get called by given argument types.

Yes; that is the point of overloading. If int vs long could make
a difference, you can overload on this difference.

The major problem, IMHO, is that implicit narrowing conversions are allowed.
Many compilers warn about them, which at least gives you an option about
finding such problems. OTOH, getting rid of implicit narrowing would break
lots of perfectly valid code, like
 int i = getchar();
 char c;
 if( i == EOF ) ... // handle eof
 else c = i; // cannot cause truncation
Once again, we are hamstrung by what I view as a significant C design error.

>> The function 'f' expects to get an int, and it gets an int. Why does
>> it care whether a valid int value happened to be stored originally
>> in some other data type, like short, or class IntRange, or for
>> that matter, long or double?
>
>OTOH, if it gets a reference to int it could care very much whether a
>temporary is used. I seem to remember this problem being addressed a
>while back.

There is a bit of confusion on this point. According to the ARM, you
can pass only an exactly matching type to a function that takes a
non-const reference parameter. (If the function takes a const ref,
it can't cause a problem.)

However, you can sometimes bind a non-const reference to a temporary,
so the exact wording of the rule still needs some work.
---
Steve Clamage, stephen.clamage@eng.sun.com






Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Thu, 26 Jan 1995 10:41:48 GMT
Raw View
In article <3fslsb$r36@engnews2.Eng.Sun.COM> clamage@Eng.Sun.COM (Steve Clamage) writes:
>pjl@graceland.att.com (Paul J. Lucas) writes:
>
>> Given that a new keyword has been added for a very narrow
>> purpose, I could see it extending to function arguments as
>> well.  Example:
>
>There was a LONG thread on exactly this topic a little while ago.
>
>IMHO it's a bad idea because the decision about what conversions
>should be allowed does not belong to the function being called,
>but to the caller.
>
>The function 'f' expects to get an int, and it gets an int. Why does
>it care whether a valid int value happened to be stored originally
>in some other data type, like short, or class IntRange, or for
>that matter, long or double? Promotion cannot cause a problem.
>A narrowing conversion might cause a problem, but only the calling
>location can possibly know.

 I beg to differ: in C++ at least. In a "proper" language,
your argument might be valid. But C++ is based on C, which is
an extremely low level and excessively powerful language.
On the other hand, C++ is supposed to be _both_ low and high level.

 Consider for example, the ghastly automatic conversions
of integral types, not to mention modular addition, and
multiplication with no well defined semantics. These things
are indeed useful for assembler level programming, but Bjarne
himself would like to deprecate the worst of these automatic conversions.

 The next best thing we could do is inhibit automatic
conversion in well designed _families_ of functions.
Consider, for example:

 template<class T> T max(T t1, T t2)
 { return t1 > t2 ? t1 : t2; }

 long a; float b;
 max(a,b); // error

Already, with templates, we can inhibit conversions.
So it is already possible to write a function which
has nasty conversions on _all_ (deducible) arguments inhibited:
just make it a specialisation of a template which provides
no definition.

In any case, the point is that some function sets  would
be designed to be used with, say, signed integers,
and we simply don't want a call with an unsigned integer
to succeed.

There is an alternative to your view that the _caller_
is reponsible for conversions: even ISO C has overloaded
user defined functions:

 void f(int);

Yes, that is NOT a single function. It is in fact an overloaded
SET of functions:

 void f(explicit int);
 void f(explicit long);
 void f(explicit double);
 void f(explicit unsigned);
 ....

It is not hard to see that implicit conversions and overloading
are in some ways equivalent. And the argument _for_ "explicit"
parameters is that it allows the programmer to choose exactly
which functions of that set are in fact defined, and ignore
the "automatic generation" the other function signatures
provided by C.

In fact, a better way to do this might be to provide the classes

 Int, Long, Double ...

in the Standard library:

 template <class T> class exact {
  T t;
 public:
  exact(explicit T a) : t(a) {}
  operator T()const { return t; } // explicit operator T()??
 };

 typedef exact<int> Int;
 typedef exact<long> Long;

 Int max(Int a, Int b) { return int(a) < int(b) ? b : a; }

 max(1,2L); // error


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: tob@world.std.com (Tom O Breton)
Date: Thu, 26 Jan 1995 20:39:46 GMT
Raw View
I write:
> I agree with the principle, but IMO it is already violated, with
> overloaded functions. Overloaded functions already control what
> conversions are legal and which functions get called by given argument
> types.

clamage@Eng.Sun.COM (Steve Clamage) writees:
> No they don't.

Sure they do:

void    foo( int, long );
foo( 0, 1 );    /* Legal */

...but...

void    foo( int, long );
void    foo( long, int );
foo( 0, 1 );    /* Illegal */

The conversion was possible before, now it isn't. The overloading
stopped it. I don't know if that was what you had in mind but it's what
I had.

> Yes; that is the point of overloading. If int vs long could make
> a difference, you can overload on this difference.

Here I'm assuming that one does not intend to handle every combination
of types, or of built-in types:

Using overloading to filter out undesired types may be a less convenient
solution, because you have to predict all the types that will be
undesired and write "competing" overloaded prototypes to catch them.

It also seems impossible in principle to filter all user-defined
conversions with this mechanism, but I think that capability may not
ever be needed.

        Tom

--
tob@world.std.com
TomBreton@delphi.com: Author of The Burning Tower





Author: jbuck@synopsys.com (Joe Buck)
Date: 23 Jan 1995 22:01:06 GMT
Raw View
Paul J. Lucas (pjl@graceland.att.com) wrote:
>>   void f( explicit int = 0, bool = false );
>>
>>  the semantics could be that the first argument *must* be an int
>>  with no standard or user conversions applied.
>
>>  Comments?  I'm not fanatical about the idea...it just popped
>>  into my head.

kevinl@fangorn.cs.monash.edu.au (Kevin Lentin) writes:
>Sounds good. One situation where it might get annoying is if you had an
>explicit long and passed an int. Or simply an int constant without the L on
>the end. How about 'mostly explicit' :-)

Yuk.  If you can't find an extremely simple way to say what "explicit"
means, don't bother.  bool->int and int->long are both integral
promotions.  If someone wrote "explicit long" it would mean they wanted
exactly a long.

You would want to allow passing by reference, and probably giving a "foo *"
value to an "explicit const foo *" argument, but nothing that did any
kind of type conversion, or it's just not "explicit".




--
-- Joe Buck  <jbuck@synopsys.com> (not speaking for Synopsys, Inc)
Phone: +1 415 694 1729




Author: carson@gwis2.circ.gwu.edu (John Carson)
Date: 23 Jan 1995 22:56:33 GMT
Raw View
Steve Clamage (clamage@Eng.Sun.COM) wrote:
: pjl@graceland.att.com (Paul J. Lucas) writes:

 : > Given that a new keyword has been added for a very narrow
: > purpose, I could see it extending to function arguments as
: > well.  Example:

: >  void f( int = 0, bool = false );

: >  f( true ); // legal due to bool->int conversion
: >
: > In the above, 'true' is mistakenly converted to int(1).  If you
: > could do this:

: >  void f( explicit int = 0, bool = false );
: >
: > the semantics could be that the first argument *must* be an int
: > with no standard or user conversions applied.


: There was a LONG thread on exactly this topic a little while ago.

: IMHO it's a bad idea because the decision about what conversions
: should be allowed does not belong to the function being called,
: but to the caller.

If you allow function delcarations to use this approach without requiring
it in the definition, then the function caller (or someone related) can
make the decision.

: The function 'f' expects to get an int, and it gets an int. Why does
: it care whether a valid int value happened to be stored originally
: in some other data type, like short, or class IntRange, or for
: that matter, long or double? Promotion cannot cause a problem.
: A narrowing conversion might cause a problem, but only the calling
: location can possibly know.
: --
: Steve Clamage, stephen.clamage@eng.sun.com

I agree, but without any way of preventing it, how do you know it has
happened?




Author: tob@world.std.com (Tom O Breton)
Date: Mon, 23 Jan 1995 23:03:23 GMT
Raw View
clamage@Eng.Sun.COM (Steve Clamage) writes:
> There was a LONG thread on [the explicit keyword extended to argument
> lists] a little while ago.
>
> IMHO it's a bad idea because the decision about what conversions
> should be allowed does not belong to the function being called,
> but to the caller.

I agree with the principle, but IMO it is already violated, with
overloaded functions. Overloaded functions already control what
conversions are legal and which functions get called by given argument
types.

Therefore it's possible `explicit' arguments may help narrow this hole
rather than widen it.

> The function 'f' expects to get an int, and it gets an int. Why does
> it care whether a valid int value happened to be stored originally
> in some other data type, like short, or class IntRange, or for
> that matter, long or double?

OTOH, if it gets a reference to int it could care very much whether a
temporary is used. I seem to remember this problem being addressed a
while back.

        Tom

--
tob@world.std.com
TomBreton@delphi.com: Author of The Burning Tower





Author: pjl@graceland.att.com (Paul J. Lucas)
Date: Sat, 21 Jan 1995 21:53:57 GMT
Raw View
 Given that a new keyword has been added for a very narrow
 purpose, I could see it extending to function arguments as
 well.  Example:

  void f( int = 0, bool = false );

  f( true ); // legal due to bool->int conversion

 In the above, 'true' is mistakenly converted to int(1).  If you
 could do this:

  void f( explicit int = 0, bool = false );

 the semantics could be that the first argument *must* be an int
 with no standard or user conversions applied.

 Comments?  I'm not fanatical about the idea...it just popped
 into my head.
--
 - Paul J. Lucas   #ifndef COMMON_KNOWLEDGE
   AT&T Bell Laboratories #include <stddisclaimer.h>
   Naperville, IL  #endif




Author: kevinl@fangorn.cs.monash.edu.au (Kevin Lentin)
Date: 22 Jan 1995 05:11:53 GMT
Raw View
Paul J. Lucas (pjl@graceland.att.com) wrote:
>   void f( explicit int = 0, bool = false );
>
>  the semantics could be that the first argument *must* be an int
>  with no standard or user conversions applied.

>  Comments?  I'm not fanatical about the idea...it just popped
>  into my head.

Sounds good. One situation where it might get annoying is if you had an
explicit long and passed an int. Or simply an int constant without the L on
the end. How about 'mostly explicit' :-)

--
[==================================================================]
[ Kevin Lentin                   |___/~\__/~\___/~~~~\__/~\__/~\_| ]
[ kevinl@bruce.cs.monash.edu.au  |___/~\/~\_____/~\______/~\/~\__| ]
[ Macintrash: 'Just say NO!'     |___/~\__/~\___/~~~~\____/~~\___| ]
[==================================================================]




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Sun, 22 Jan 1995 11:36:26 GMT
Raw View
pjl@graceland.att.com (Paul J. Lucas) writes:

> Given that a new keyword has been added for a very narrow
> purpose, I could see it extending to function arguments as
> well.

Sure, it _could_ be done - but would it be useful?
C++ is plenty complicated enough already, I don't think the
benefit/cost ratio for this suggestion is sufficient.

> Comments?  I'm not fanatical about the idea...it just popped
> into my head.

Give 'em a new keyword and they take a mile ;-)

--
Fergus Henderson - fjh@munta.cs.mu.oz.au
all [L] (programming_language(L), L \= "Mercury") => better("Mercury", L) ;-)




Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 22 Jan 1995 04:10:51 GMT
Raw View
pjl@graceland.att.com (Paul J. Lucas) writes:

> Given that a new keyword has been added for a very narrow
> purpose, I could see it extending to function arguments as
> well.  Example:

>  void f( int = 0, bool = false );

>  f( true ); // legal due to bool->int conversion
>
> In the above, 'true' is mistakenly converted to int(1).  If you
> could do this:

>  void f( explicit int = 0, bool = false );
>
> the semantics could be that the first argument *must* be an int
> with no standard or user conversions applied.


There was a LONG thread on exactly this topic a little while ago.

IMHO it's a bad idea because the decision about what conversions
should be allowed does not belong to the function being called,
but to the caller.

The function 'f' expects to get an int, and it gets an int. Why does
it care whether a valid int value happened to be stored originally
in some other data type, like short, or class IntRange, or for
that matter, long or double? Promotion cannot cause a problem.
A narrowing conversion might cause a problem, but only the calling
location can possibly know.
--
Steve Clamage, stephen.clamage@eng.sun.com




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sun, 22 Jan 1995 19:24:03 GMT
Raw View
In article <9502222.1627@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
>pjl@graceland.att.com (Paul J. Lucas) writes:
>
>> Given that a new keyword has been added for a very narrow
>> purpose, I could see it extending to function arguments as
>> well.
>
>Sure, it _could_ be done - but would it be useful?

 Actually, yes, I think so. It would be _more_ useful
as a more general notion. Consider for example:

 class string { public:
  string(explicit char); // string of one char
  explicit string(explicit int n, explicit char=' ');
   // string of n chars
 };
 unsigned char ch='*';
 string s = ch; // error
 string s = char(ch); // OK
 string s = 1; // error
 string s(1); // OK
 string s(1,32); // error
 string s(1,'*'); // OK

Suppressing conversions reduces ambiguity problems with overloading.
Another example is:

 int max(explicit int, explict int);
 long a=1000000L, b = 20000000L;
 long c = max(a,b); // error
 unsigned aa=~0, bb=0;
 unsigned cc = max(aa,bb); // error

This ensures you do not get trapped by unintended conversions,
such as a positive unsigned int to a negative int.
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189