[comp.std.c++] new[] and constructor arguments

rpk@rice-chex.ai.mit.edu (Robert Krajewski) (09/13/90)

Does anybody else consider the fact that new[] only works with default
constructors (i.e., those with no parameters) to be a blemish on the
language ?  If you spend some time generate a nice simple consistent
class, and then discover you need to use new[], you have to then go
and implement modification operations that may be have been
unneccessary before.

There are workarounds, I realize, like writing your own loop and using
operator new (size_t, void *).  Basically, I don't buy the argument in
the bulletted item on page 61 of E&S (section 5.3.3).  The compiler
can easily generate correct and efficient initialization code for this
case where parameters are passed.  The only decision to be made is how
many times the arguments to a constructor are evaluated in such a
context.


Robert P. Krajewski
Internet: rpk@ai.mit.edu ; Lotus: robert_krajewski.lotus@crd.dnet.lotus.com

pcg@cs.aber.ac.uk (Piercarlo Grandi) (09/16/90)

On 12 Sep 90 17:57:01 GMT, rpk@rice-chex.ai.mit.edu (Robert Krajewski) said:

rpk> Does anybody else consider the fact that new[] only works with
rpk> default constructors (i.e., those with no parameters) to be a
rpk> blemish on the language ? [ ... ]

I think that the special treatment of arrays by operator new is the big
blemish.

The problem here is really that operator new really should allocate a
single object; if you use cast like parenthesization of abstract
declarators, you see that in

	typedef c100 complex[100];

	complex *c1 = new complex[100];
	complex *c2 = new c100;
	complex *c3 = new (complex[100]);

the last three lines *should be* exactly equivalent, and essentially
equivalent in turn to the last two lines of

	extern "C" extern void *malloc(size_t);

	complex *c4 = (complex *) malloc(sizeof (complex[100]));
	complex *c5 = (complex *) malloc(sizeof (c100));

because 'c100' is not a class type and therefore has no constructors or
destructors.

rpk> There are workarounds, I realize, like writing your own loop and
rpk> using operator new (size_t, void *).

This is not a workaround; it is a proper use of the abstraction
facilities of C++.

A strict interpretation like mine above would mean that users should be
made to define their own array type constructors; in other words, users
should make the 'c100' type explicit as a class, with a suitable
constructor to initialize each element, e.g. something like

	#include <complex.H>

	class cv
	{
	protected:
	    const unsigned n; complex *c;
	public:
	    cv(unsigned /* add further parameters as desired */); ~cv();
	    inline complex &operator[](unsigned);
	    // .....
	};

	#include <assert.H>

	// Under C++ 2.1 the static below regrettably is out
	static inline complex &cv::operator[](auto const unsigned i)
	{
	    assert (i >=0 && i < this->n,"cv::operator[]");
	    return &this->c[i];
	}

	extern "C" extern void *calloc(size_t,unsigned);
	extern "C" extern void free(void *);

	extern cv::cv(auto const unsigned e /* any other parameters */)
	: n(e), c(calloc(sizeof (complex),e))
	{
	    register complex *c;
	    register complex *const l = &this->c[e];

	    assert (this->c != 0,"cv::cv");

	    for (c = &this->c[0]; c < l; c++)
		(void) new((void *) c) (complex)(/* any other parameters */);
	}

	extern cv::~cv()
	{
	    register complex *c;
	    register complex *const l = &this->c[e];

	    for (c = &this->c[0]; c < l; c++)
		c->~complex();

	    free((void *) c);
	}

	// ... and so on merrily

In the absence of templates, this has been deemed too boring for users
to do for each type of array they want (even if you can get a lot of
mileage out of macros and using 'void *' and horrible pointer
arithmetic), so operator new does a special case for arrays, leading to
all that silly square bracketing with operator delete.

    Note that C++ 2.1 seems to imply that the above is more or less how
    the compiler is required to implement heap arrays now. Uhm.

rpk> Basically, I don't buy the argument in the bulletted item on page
rpk> 61 of E&S (section 5.3.3).  The compiler can easily generate
rpk> correct and efficient initialization code for this case where
rpk> parameters are passed.

The fundamental problem is that C++ inherits from C a weak definition of
arrays and the array type constructor, one that seems to be unacceptable
to users of C++ (which seem to require a definition of arrays that
implies keeping descriptors at run time); attempts to patch this
definition up so that it meets the expectation of C++ users without
jettisoning the C one altogether have caused quite a bit of confusion.

My idea: if the C++ users want arrays that behave like classes, let them
define their own array classes, and when they will have templates, they
can define just one template for each array arity.

XOR, junk the conventional C idea of the array type constructor, and
provide a C++-ish one. Halfway you basically get mostly trouble.

Incidentally, making the main argument to operator new be a parenthesis
enclosed abstract declarator (a cast), just like for the sizeof
operator, would remove one of the two most prominent causes of nasty
syntax ambiguities in C++.
--
Piercarlo "Peter" Grandi           | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk