Topic: Aggregate initialization vs. copy constructor


Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Mon, 18 Apr 1994 10:03:22 GMT
Raw View
In article <2o4ag1$esm@fsgi01.fnal.gov> b91926@fsgi01.fnal.gov (David Sachs) writes:
>
>Apparently, the currently used interpretation is that ANY initializer
>for an aggregate-eligible class that starts with a "{" is treated
>as an aggregate initializer. At some point the standards committe
>might decide otherwise.

I hope so, because unless I am mistaken, that would break a lot of existing
ANSI C code.

(In C, initializers may have excess curley-braces around them.)

Actually, I think this whole part of the language (i.e. braced initializer
syntax and semantics) has been pretty badly specified.

The last time I looked at an x3j16/wg21 working paper, the whole description
of braced and unbraced initializers (8.5) bore NO RESEMBLANCE whatsoever to
its (carefully crafted) ANSI C counterpart... the result being that a whole
lot of the C++ language specification for initializers was self-contradictory
and downright nonsensical.

For example, it begins by saying:

 "Automatic, register, and external variables at file scope may
 be initialized by arbitrary expressions..."

Obviously, that means that in C++, the following is legal at file-scope:

 auto char *cp = 99;

Hey!  You read it!  Didn't it say ``arbitrary expressions''??

This is just one example (well... two actually) of the kind of gibberish
this whole section is filled with.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: pkt@lpi.liant.com (Scott Turner)
Date: Wed, 13 Apr 1994 18:01:23 GMT
Raw View
In article <9404121838.AA07872@ses.com>, jamshid@ses.com (Jamshid Afshar)
wrote:

> In article <pkt-040494151553@138.52.2.129> pkt@lpi.liant.com (Scott Turner)
> wrote:
> >In article <9404011850.AA15588@ses.com>, jamshid@ses.com (Jamshid Afshar)
> >wrote:
> >> class complex {
> >> public:
> >>     double real, imag;
> >> };
> >> complex a[] = { c,c };  // g++ 2.5.5 fails to flag error that `c' is
> >>                         // not a valid initializer for complex::real

> >It is more sensible to read the sentence you quote as:
> >
> >   An aggregate is (an array) or (an object of a class with no
> >   user-declared constructors, no private or protected members, [...])

> That *is* how I'm reading the sentence.

Sorry I misunderstood you.

> I do see a source for possible confusion.  The WP says in paragraph 3
> that "An aggregate that is a class may also be initialized with an
> object of its class or of a class publicly derived from it".  This
> sentence seems to allow the code, but the next paragraph states
> "Braces may be elided as follows. [...]  If however the
> initializer-clause of a subaggregate does not begin with a left brace,
> then only enough elements from the list are taken to account for the
> members of the aggregate [...]".

You're right; that is where we differ.

> Because there is no discussion about ambiguity resolution in the
> following code, I believe paragraph 3 is only intended to apply to the
> initialization of a complete aggregate and does not apply when the
> class object is a subaggregate.

Parapgraph 3 is redundant with 12.8 and other parts of the WP/ARM
which say that a class object may be initialized with another class
object.  IMO it is best understood as a mere cross-reference, because
an aggregate is just a class which admits a special form of
initializer (i.e. brace-enclosed list of member values).  The other
form of initializer applies to all classes (including aggregates).

Thus, one effect of your interpretation would be that a value in
a brace-enclosed initializer can initialize an object, except if
the object is an aggregate.  That makes aggregates second class.
And for what purpose?  So that braces can be elided!  You're favoring
syntactic sugar over a uniform ability to initialize one object
with another.

Such a question is not settled by reading something into the ARM.
The ARM contains several ambiguity resolutions, all of which were
added in conjunction with visible debate in the C++ community.  The
visibility of this aggregate vs. object initialization ambiguity
has not reached that level.

> Btw, although g++ erroneously compiles the original code,

It's unjust to call g++ "erroneous" for accepting code which is
directly sanctioned by the ARM and WP.  The unfortunate aspect
of the original code is that a possible resolution of an ambiguity
in _other_ code would rule this code invalid.
--
Scott Turner
Liant Software Corp.
959 Concord St., Framingham, MA 01701  USA
(508)872-8700
pkt@lpi.liant.com




Author: pkt@lpi.liant.com (Scott Turner)
Date: Thu, 31 Mar 1994 17:38:32 GMT
Raw View
In article <9403252101.AA07566@ses.com>, jamshid@ses.com (Jamshid Afshar)
wrote:

> :: class complex {
> :: public:
> ::     double real, imag;
> :: };
> ::
> :: int main() {
> ::     complex c;
> ::     complex a[] = { c,c };  // g++ 2.5.5 fails to flag error that `c' is not
> ::                             // a valid initializer for complex::real
> :: }
> ::
> :
> :Since class complex has default copy constructor, we can use
> :ARM 12.6.1 pp. 289 to declare the above code valid.
>
> Why do you think complex has a default copy constructor?

Although the ARM is equivocal about whether complex has a copy constructor,
the C++ standards committee resolved a year or so ago that it does.
This is reflected in the current working paper right where one would
expect it, in section 12.1.

    If no copy constructor is declared in the class definition, a copy
    constructor is implicitly declared.  The defintion for an implicitly-
    declared copy constructor is generated only if that copy constructor
    is called.
--
Scott Turner
Liant Software Corp.
959 Concord St., Framingham, MA 01701  USA
(508)872-8700
pkt@lpi.liant.com




Author: jamshid@ses.com (Jamshid Afshar)
Date: 1 Apr 1994 13:05:32 -0600
Raw View
In article <pkt-310394123833@138.52.2.129>,
Scott Turner <pkt@lpi.liant.com> wrote:
:In article <9403252101.AA07566@ses.com>, jamshid@ses.com (Jamshid Afshar)
:wrote:
:>:: class complex {
:>:: public:
:>::     double real, imag;
:>:: };
:>:: int main() {
:>::     complex c;
:>::     complex a[] = { c,c };  // g++ 2.5.5 fails to flag error that `c' is
:>::                             // not a valid initializer for complex::real
:>:: }
:>:Since class complex has default copy constructor, we can use
:>:ARM 12.6.1 pp. 289 to declare the above code valid.
:>
:> Why do you think complex has a default copy constructor?
:
:Although the ARM is equivocal about whether complex has a copy constructor,
:the C++ standards committee resolved a year or so ago that it does.
:This is reflected in the current working paper right where one would
:expect it, in section 12.1.
:
:    If no copy constructor is declared in the class definition, a copy
:    constructor is implicitly declared.  The defintion for an implicitly-
:    declared copy constructor is generated only if that copy constructor
:    is called.

Thanks for the clarification.  This language isn't in my June 1993
copy of the WP, but it is in my Januaray 1994 copy.  But, to make sure
there's no misunderstanding, my above bug report is still valid
because along with the chage/clarification that ``all classes have a
copy constructor'' (the distinction is now implicitly- vs. user-
declared), ANSI/ISO changed ARM 8.4.1 Aggregates from:

 An aggregate is an array or an object of a class with no
 constructors, no private or protected members, [...]

to Jan 1994 WP 8.5.1 Aggregates:

 An aggregate is an array or an object of a class with no
 user-declared constructors, no private or protected members, [...]
 ^^^^^^^^^^^^^

By any standard, a compiler failing to flag the above code as an error
needs a bug fix.

Jamshid Afshar
jamshid@ses.com




Author: b91926@fsgt01.fnal.gov (David Sachs)
Date: 1 Apr 1994 15:53:44 -0600
Raw View
The following pair of program clearly illustrate the difference
between aggregate and "normal C++" initialization. Although they
are very similar, their outputs differ markedly.

#define VIRTUAL virtual
// rest of program 2 is the same as program 1
#include <iostream.h>

class complex
{
  public:
  double re, im;
  VIRTUAL operator double() {return re+im;}
};

complex x;

int main()
{
  x.re = 1.0;
  x.im = 1.0;

  complex xx[2]={x,x};

  cout << xx[0].re << " " << xx[1].re << endl;
  return 0;
}




#define VIRTUAL
// rest of program 2 is the same as program 1
...



This distinction was the point of a recent ad for PC LINT (Gimpel)


Please excuse typos.




Author: pkt@lpi.liant.com (Scott Turner)
Date: Mon, 4 Apr 1994 19:15:53 GMT
Raw View
In article <9404011850.AA15588@ses.com>, jamshid@ses.com (Jamshid Afshar)
wrote:

> In article <pkt-310394123833@138.52.2.129>,
> :>::     complex a[] = { c,c };  // g++ 2.5.5 fails to flag error that `c' is
> :>::                             // not a valid initializer for complex::real

>  An aggregate is an array or an object of a class with no
>  user-declared constructors, no private or protected members, [...]
>  ^^^^^^^^^^^^^
>
> By any standard, a compiler failing to flag the above code as an error
> needs a bug fix.

It is more sensible to read the sentence you quote as:

   An aggregate is (an array) or (an object of a class with no
   user-declared constructors, no private or protected members, [...])

The above code is an example of aggregate initialization for "an array".
IMO a compiler must accept the above code, and perhaps the working paper
needs some editorial clarification.
--
Scott Turner
Liant Software Corp.
959 Concord St., Framingham, MA 01701  USA
(508)872-8700
pkt@lpi.liant.com




Author: pkt@lpi.liant.com (Scott Turner)
Date: Mon, 4 Apr 1994 20:08:17 GMT
Raw View
In article <2ni558$56a@fsgt01.fnal.gov>, b91926@fsgt01.fnal.gov (David
Sachs) wrote:

> The following pair of program clearly illustrate the difference
> between aggregate and "normal C++" initialization. Although they
> are very similar, their outputs differ markedly.

After a slow start, at last I'm into this example.  It's the
original Gimpel version which is most interesting to me.

  #define VIRTUAL
> // rest of program 2 is the same as program 1
> #include <iostream.h>
>
> class complex
> {
>   public:
>   double re, im;
>   VIRTUAL operator double() {return re+im;}
> };
>
> complex x;
>
> int main()
> {
>   x.re = 1.0;
>   x.im = 1.0;
>
>   complex xx[2]={x,x};
>
>   cout << xx[0].re << " " << xx[1].re << endl;
>   return 0;
> }

David Sachs (and I'm sure several compilers) interpret this program
as printing "2 0".  Class complex is an aggregate, and aggregates may
be initialized member by member, as per 8.4.1 of the ARM and 8.5.1 of
the present working paper.  So in
>   complex xx[2]={x,x};
The first 'x' initializes the real part of xx[0], the second 'x'
initializes the imaginary part of xx[0], and the two data members of
xx[1] go uninitialized.

But note that the ARM and working paper do _not_ retrict aggregates
to only this form of initializer.   If an aggregate is a class, can it
not be initialized the way a non-aggregate class is initialized (assuming
the classes concerned have copy constructors)?

So the Gimpel example shown above is ambiguous.  IMO it is preferable to
interpret the program above on the basis that the explicit curly-braces
match the structure being initialized.  In other words, the programmer
has not elided curly braces, and the program should print "1 1".

What I've said so far may be moot, but it's certain that this is a fine
example of what a C++ Lint should catch.
-------
Scott Turner
Liant Software Corp.
959 Concord St., Framingham, MA 01701  USA
(508)872-8700
pkt@lpi.liant.com





Author: b91926@fsgi01.fnal.gov (David Sachs)
Date: 8 Apr 1994 14:15:13 -0500
Raw View
pkt@lpi.liant.com (Scott Turner) writes:

...

>David Sachs (and I'm sure several compilers) interpret this program
>as printing "2 0".  Class complex is an aggregate, and aggregates may
>be initialized member by member, as per 8.4.1 of the ARM and 8.5.1 of
>the present working paper.  So in
>>   complex xx[2]={x,x};
>The first 'x' initializes the real part of xx[0], the second 'x'
>initializes the imaginary part of xx[0], and the two data members of
>xx[1] go uninitialized.

Actually the rules for aggregate initialization require that the
members of xx[1] be initialized to 0. This applies in any case
where there are an insufficient number of initializers for
an aggregate.

>But note that the ARM and working paper do _not_ retrict aggregates
>to only this form of initializer.   If an aggregate is a class, can it
>not be initialized the way a non-aggregate class is initialized (assuming
>the classes concerned have copy constructors)?

>So the Gimpel example shown above is ambiguous.

...

Apparently, the currently used interpretation is that ANY initializer
for an aggregate-eligible class that starts with a "{" is treated
as an aggregate initializer. At some point the standards committe
might decide otherwise.

Naturally any aggregate-eligible class will have a (public default
compiler generated) copy constructor.

It is very easy to construct a case where allowing both forms, could
require rescanning a long initializer list:

class d;  // pre-declare

class m
{
  public:
  operator d();
  // other members go here
};

class d
{
  public:
  m member;
};

m m1;
d d1;

d da[10]={m1,m1,m1,m1,m1,m1,m1,m1,m1,d1};  // what ???




Author: jamshid@ses.com (Jamshid Afshar)
Date: 12 Apr 1994 13:38:26 -0500
Raw View
In article <pkt-040494151553@138.52.2.129> pkt@lpi.liant.com (Scott Turner)
wrote:
>In article <9404011850.AA15588@ses.com>, jamshid@ses.com (Jamshid Afshar)
>wrote:
>> class complex {
>> public:
>>     double real, imag;
>> };
>> complex a[] = { c,c };  // g++ 2.5.5 fails to flag error that `c' is
>>                         // not a valid initializer for complex::real
>>
>>  [WP] An aggregate is an array or an object of a class with no
>>  user-declared constructors, no private or protected members, [...]
>>  ^^^^^^^^^^^^^
>> By any standard, a compiler failing to flag the above code as an error
>> needs a bug fix.
>
>It is more sensible to read the sentence you quote as:
>
>   An aggregate is (an array) or (an object of a class with no
>   user-declared constructors, no private or protected members, [...])
>
>The above code is an example of aggregate initialization for "an array".
>IMO a compiler must accept the above code, and perhaps the working paper
>needs some editorial clarification.

That *is* how I'm reading the sentence.  `a' is an aggregate, but so
are its elements because `complex' does not have any user-defined
constructors or non-public members.  Jan WP 8.5.1 paragraph 1: "When
an aggregate is initialized the initializer may be an
initializer-clause consisting of a brace-enclosed, comma-separated
list of initializers for the members of the aggregate, written in
increasing subscript or member order.  If the aggregate contains
subaggregates, this rule applies recursively to the members of the
subaggregate".  This paragraph also discusses the zero padding rule.

I do see a source for possible confusion.  The WP says in paragraph 3
that "An aggregate that is a class may also be initialized with an
object of its class or of a class publicly derived from it".  This
sentence seems to allow the code, but the next paragraph states
"Braces may be elided as follows. [...]  If however the
initializer-clause of a subaggregate does not begin with a left brace,
then only enough elements from the list are taken to account for the
members of the aggregate [...]".

Because there is no discussion about ambiguity resolution in the
following code, I believe paragraph 3 is only intended to apply to the
initialization of a complete aggregate and does not apply when the
class object is a subaggregate.

 class complex {
 public:
    double real, imag;
 };
 struct A {
    operator double() { return 0; }
    operator complex() { complex r; return r; }
 } a;
 complex arr[] = { a,a,a };  // I believe arr has 2 elements

Btw, although g++ erroneously compiles the original code, it does get
this right while Cfront fails with "sorry not implemented: general
initializer in initalizer list".

Jamshid Afshar
jamshid@ses.com




Author: jorgej@avs.cs.rpi.edu (Joaquim Jorge)
Date: 26 Mar 1994 00:50:39 GMT
Raw View
> :Since class complex has default copy constructor, we can use
> :ARM 12.6.1 pp. 289 to declare the above code valid.
>
> Why do you think complex has a default copy constructor?  I admit the
> ARM is not very clear about when default (aka "compiler generated")
> constructors, destructors and assignment operators are created, but
> the 12.6.1 commentary should make it clear that the above complex
> class does not have a copy-ctor: "Note that SINCE class complex has
> constructors it is considered to have a public copy constructor with
> the default implementation even though no copy constructor is
> explicitly declared (f12.1)".

Section 12.8 pp295 (ARM 1990 ed) gives a different statement about when
default copy constructors and assignment operators are generated:

"The programmer may define one or both of these. If not defined by the
programmer, they will be defined as memberwise assignement and memberwise
initialization of the members of [class] X respectively.
If all bases and members of X have copy constructors accepting const arguments,
the generated copy constructor will [have as prototype] X::X(const X&)"

The above is unqualified, which I would take as implying that the example
given is a legal C++ program. Has this been changed ?

:: class complex {
:: public:
::     double real, imag;
:: };
::
:: int main() {
::     complex c;
::     complex a[] = { c,c };  // g++ 2.5.5 fails to flag error that `c' is not
::                             // a valid initializer for complex::real
:: }
--
Joaquim Jorge
======================================================================
Rensselaer Polytechnic Institute  | E-mail: jorgej@cs.rpi.edu
C.S. Department                   | Phone:  (518) 276 4850
RPI,Troy, NY 12180    | Home:   (518) 273 4415
======================================================================