[comp.lang.c++] The type yielded by "new T[expression]"

rfg@paris.ics.uci.edu (Ronald Guilmette) (03/21/90)

I believe that there exists a ghastly violation of strong typing rules
currently built into the C++ language.  I'd like somebody to explain
this to me and defend it (if possible).

First, some background.  Note that a type which is "pointer-to-T" is very
different from a type which is "pointer-to-array-of-T".  If you don't believe
it, just try compiling this:

	int *ip;
	int (*iap)[];

	void foo ()
	{
		ip = iap;	// gets error
		iap = ip;	// gets error;
	}

Now the question I'm concerned about is: "What is the type of the value
yielded by:

	new T[size];

Currently (it seems) the value yielded by an "array new" operation like
this is treated as being of type "pointer-to-T".  This seems highly
nonsensical to me.  It would make far more sense if the type of value
yielded were "pointer-to-array-of-T" when the "new" invocation includes
a (square-bracketed) size expression.

Does anyone disagree?  If so, please state your reasoning.

One minor footnote.  It was formerly the case that any attempts to "delete"
arrays of objects had to include a square-bracketed size expression in
the delete statement, i.e.:

	delete [size] ptr;

It appears that this has changed recently, and that the proper way to do
this now is simply:

	delete [] ptr;

This also appears highly nonsensical to me.  Why not just allow typing
information to determine the type of delete operation to be performed.
For example, if the type of "ptr" is "pointer-to-T" then the statement:

	delete ptr;

would delete a single object (as has traditionally been the case).  However
if the type of "ptr" is "pointer-to-array-of-T", then:

	delete ptr;

would (obviously) need to delete an array of objects.

Am I making too much sense?


// Ron Guilmette (rfg@ics.uci.edu)
// C++ Entomologist
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

ark@alice.UUCP (Andrew Koenig) (03/21/90)

In article <26068361.27247@paris.ics.uci.edu>, rfg@paris.ics.uci.edu (Ronald Guilmette) writes:
> I believe that there exists a ghastly violation of strong typing rules
> currently built into the C++ language.  I'd like somebody to explain
> this to me and defend it (if possible).

Not particularly ghastly, but rather a minor convenience that
is well described in the manual.

> Now the question I'm concerned about is: "What is the type of the value
> yielded by:

> 	new T[size];

The type is `pointer to T' for any `new' statement with this syntax.

> Currently (it seems) the value yielded by an "array new" operation like
> this is treated as being of type "pointer-to-T".  This seems highly
> nonsensical to me.  It would make far more sense if the type of value
> yielded were "pointer-to-array-of-T" when the "new" invocation includes
> a (square-bracketed) size expression.

Impossible, because `size' doesn't need to be a constant.
If you want a pointer to array of, say, 17 T, you can write

	new T[17][1]

which will get you exactly that.
-- 
				--Andrew Koenig
				  ark@europa.att.com

rfg@ics.uci.edu (Ronald Guilmette) (03/21/90)

In article <26068361.27247@paris.ics.uci.edu> rfg@paris.ics.uci.edu (Ronald Guilmette) writes:
>I believe that there exists a ghastly violation of strong typing rules
>currently built into the C++ language.  I'd like somebody to explain
>this to me and defend it (if possible).

---------------------------------------------------------------------------
// This is a follow-up to my previous posting regarding new and delete
// on arrays.  The current situation is much worse that I thought.

// Consider the following simple program:

	extern "C" int printf (const char *, ...);

	class class0 {
	public:
	  class0 ();
	  ~class0 ();
	};

	class0::class0 () { printf ("constructor called\n"); }

	class0::~class0 () { printf ("destructor called\n"); }

	typedef class0 array_of_class0[];	// unknown dimension
	typedef class0 array_of_3_class0[3];	// known dimension

	array_of_class0 *p1;
	array_of_3_class0 *p2;

	int main ()
	{
	  //p1 = (array_of_class0 *) new array_of_class0;
	  p2 = (array_of_3_class0 *) new array_of_3_class0;

	  delete p1;
	  delete p2;

	  return 0;
	}

// The first invocation of "new" (i.e. the one commented out above) is
// clearly nonsensical and should be illegal.  There is no way for the
// compiler to know how big an array to allocate.

// Both cfront (2.0) and g++ (1.37.1) flag this as a error (if it is
// uncommented).  That's the good news.  The bad news is that I can't
// find where in the manual the illegality of such statements is documented.

// The second invocation of "new" is perfectly legal, and both cfront and
// g++ accept it.

// As I noted in my previous posting however, I don't feel that the cast
// should be necessary (but it currently is).  I prefer a simple rule
// that says that "new T" always yields a value of type "T*" regardless
// of whether or not type T happens to be an array type.

// When executed, the second invocation of "new" should print the string
// "constructor called" three times.  When compiled with cfront, this is
// exactly what happens.  When compiled with g++, the "constructor called"
// message is only printed once.  This is obviously a bug in g++.

// The two delete statements are both rejected by cfront 2.0.  It says:
// "error: delete of array of arrays".  This is clearly a bug in cfront.

// g++ rejects the first delete statement only, issuing the message:
// "invalid use of array with unspecified bounds".

// g++ accepts the second delete statement, but the run-time effects are
// not what you would expect.  Execution of this statement does not cause
// *any* "destructor called" messages to be printed.  In my opinion,
// execution of this statement should cause three "destructor called"
// messages to be printed.

// In my opinion, *both* delete statements should be legal C++.  Execution
// of the first delete statement should have the same effect as:
//
//		delete [] (class0 *) p1;

// Obviously, more work is needed on refining these points on the language
// definition.

mgardi@watserv1.waterloo.edu (Mutual Life) (03/22/90)

In article <26068361.27247@paris.ics.uci.edu> rfg@paris.ics.uci.edu (Ronald Guilmette) writes:
>One minor footnote.  It was formerly the case that any attempts to "delete"
>arrays of objects had to include a square-bracketed size expression in
>the delete statement, i.e.:
>
>	delete [size] ptr;
>
>It appears that this has changed recently, and that the proper way to do
>this now is simply:
>
>	delete [] ptr;
>
I came across some interesting code in the C++ work conference proceedings.
here goes:

str = new char[length + 1];

...
delete [length + 1] str;

I assume this is not necessary (is it correct?) for built in types....

Also, in the string class mentioned in the 'first course in c++', they
do the following:

String&
String::operator=(const String& right)
	{
	if (this == &right)
		return *this;

well...my compiler (zortech) complains about this (appropriately I 
think?) and I have to use a temporary 'const String temp'
to do the comparison to 'right'. Is this the way it should be? Why the
problem...I am taking an address of a const, but not for assignment...
p.

Peter DeVries
Mutual Life of Canada
c/o mgardi@watdcsu
(519) 888-3523
(416) 972-0594

My opinions/comments are mine, and mine only, and have nothing
to do with what Mutual Life of Canada thinks (now isn't that
an understatement!)
p.

rfg@ics.uci.edu (Ronald Guilmette) (03/22/90)

In article <10605@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
>In article <26068361.27247@paris.ics.uci.edu>, rfg@paris.ics.uci.edu (Ronald Guilmette) writes:
>
>> Currently (it seems) the value yielded by an "array new" operation like
>> this is treated as being of type "pointer-to-T".  This seems highly
>> nonsensical to me.  It would make far more sense if the type of value
>> yielded were "pointer-to-array-of-T" when the "new" invocation includes
>> a (square-bracketed) size expression.
>
>Impossible, because `size' doesn't need to be a constant.

It is most definitely *not* impossible as you claim.  I believe that you
are assuming that any "pointer-to-array" type must be a "pointer-to-bounded-
array" type.  This is *not* true.

For example, what I proposed was to allow:

	typedef T array_of_T[];		// unspecified bound

	void foo ()
	{
		array_of_T *p;		// already perfectly legal!

		p = new T[100];
	}

In effect, I'm suggesting that the type of value returned by "new T[size]"
be "pointer-to-GENERIC-array-of-T" not "pointer-to-SPECIFIC-array-of-T".

As noted above, such "generic array" types already exist in the language.
Admitedly, you cannot just declare objects of "generic" (or "unbounded")
array types, but you can point to (and refer to) such objects using pointers
like the variable "p" declared in the example above.

>If you want a pointer to array of, say, 17 T, you can write
>
>	new T[17][1]
>
>which will get you exactly that.

Thanks Andrew, but I wasn't looking for a workaround.  I was just suggesting
that there exists an unnecessary hole in the C++ type system, and I was
suggesting a simple way to fix it.

P.S.  I appologize to all for the unnecessary arrogance of my prior posting.
I lose my perspective sometimes and I forget that language issue are not
worth going to war over.  Everyone should feel free to kick me if I forget
again.


// Ron Guilmette (rfg@ics.uci.edu)
// C++ Entomologist
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.