Topic: simple std::pair question


Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/10/11
Raw View
gurnec@my-dejanews.com wrote:
>
> In article <slrn6vd836.3i3.sbnaran@localhost.localdomain>,
>   sbnaran@KILL.uiuc.edu wrote:
> >
> > On 7 Oct 1998 15:05:51 GMT, Michael McKibben <mckibben@cerf.net> wrote:
> >
> > >    // default constructor
> > >    pair() : first(T1()), second(T2()) {}
> >
> > Necessary for cases where T1 and T2 are builtin types.  When you create
> > a builtin type which is not static or global, the default ctor is not
> > called.  The default ctor for a fundamental type initializes the value
> > to zero.  Suppose T1==int, T2==int
> >
> > struct pair { ...;   pair() : first(), second() {}   };
> > int main() {
> >      pair p; // p.first and p.second are not set to 0
>
> Not true, this notation does default-initialize the members first and second
> no matter what their types.  "default-initialize" for a fundamental type
> means zero-initializing it.  If the initialization list was all together left
> out, then the members would be of indeterminate values.

I'd thought they were uninitialized in both cases (making that
copy trick necessary), however I found (to my big surprise) that
in CD2, they are defined in both cases:

  12.6.2  Initializing bases and members               [class.base.init]

[...]

3 The  expression-list  in  a  mem-initializer is used to initialize the
  base class or nonstatic data member subobject denoted by the  mem-ini-
  tializer-id.  The semantics of a mem-initializer are as follows:

  --if  the  expression-list of the mem-initializer is omitted, the base
    class or member subobject is default-initialized (see _dcl.init_);

  --otherwise, the subobject indicated by mem-initializer-id is  direct-
    initialized   using   expression-list   as   the   initializer  (see
    _dcl.init_).

This shows that omitting the initializer at all causes default
initialisation. To see what the member() form does, one has to look
at dcl.init:

  8.5  Initializers                                           [dcl.init]

[...]

7 An  object whose initializer is an empty set of parentheses, i.e., (),
  shall be default-initialized.

Since member() has those parentheses, it's a default initialisation
as well.

Finally, default initialisation is indeed zero-initialisation for PODs,
as seen in 8.5/5:

  To default-initialize an object of type T means:

  --if  T is a non-POD class type (_class_), the default constructor for
    T is called (and the initialization is ill-formed if T has no acces-
    sible default constructor);

  --if T is an array type, each element is default-initialized;

  --otherwise, the storage for the object is zero-initialized.

So it seems that - at least in CD2 - you cannot get an unintialized
object member if you have an explicit constructor, and POD members
are always zero-initialized if not explicitly initialized to
something else.

Therefore, the extra copy is indeed unnecessary (as is the complete
initializer list, for that matter).

Except, of course, if this has changed since CD2 (has it?)

[...]
---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@localhost.localdomain.COM (Siemel Naran)
Date: 1998/10/12
Raw View
On 11 Oct 98 11:02:23 GMT, Andrew Koenig <ark@research.att.com> wrote:
>Michael McKibben <mckibben@cerf.net> wrote:

>> I am trying to understand why the default constructor for std::pair uses
>> copy constructors for the template arguments instead of just their
>> respective default constructor.

>So that (for instance) pair<int, int>().first() is guaranteed to be 0.

No, in a previous message on this thread, we found out that leaving out
the copy ctor still forces default initialization (see FORM2).
Specifically, for pair<int,int>


   pair() : first(first_type()), second(second_type()) {} // FORM1
   // 'first' and 'second' will be zero initialized

   pair() : first(), second() {} // FORM2
   // 'first' and 'second' will be zero initialized

   pair() {} // FORM3
   // 'first' and 'second' not zero initialized


Hence, whenever first_type or second_type or both are fundamental types,
then FORM1 or FORM2 are correct.

The question is why is FORM1 preferred over FORM2 in the standard?


--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: gurnec@my-dejanews.com
Date: 1998/10/12
Raw View
Subjects in this NG should never start with the word "simple"; no matter how
simple the question, the answer almost always seems to get complicated :-)

In article <361E38AD.AB300001@physik.tu-muenchen.de>,
  Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:
> gurnec@my-dejanews.com wrote:
> >
> > In article <slrn6vd836.3i3.sbnaran@localhost.localdomain>,
> >   sbnaran@KILL.uiuc.edu wrote:

<clip>

> > > to zero.  Suppose T1==int, T2==int
> > >
> > > struct pair { ...;   pair() : first(), second() {}   };
> > > int main() {
> > >      pair p; // p.first and p.second are not set to 0
> >
> > Not true, this notation does default-initialize the members first and second
> > no matter what their types.  "default-initialize" for a fundamental type
> > means zero-initializing it.  If the initialization list was all together
left
> > out, then the members would be of indeterminate values.

> I'd thought they were uninitialized in both cases (making that
> copy trick necessary), however I found (to my big surprise) that
> in CD2, they are defined in both cases:

>   12.6.2  Initializing bases and members               [class.base.init]

> [...]

> 3 The  expression-list  in  a  mem-initializer is used to initialize the
>   base class or nonstatic data member subobject denoted by the  mem-ini-
>   tializer-id.  The semantics of a mem-initializer are as follows:

>   --if  the  expression-list of the mem-initializer is omitted, the base
>     class or member subobject is default-initialized (see _dcl.init_);

>   --otherwise, the subobject indicated by mem-initializer-id is  direct-
>     initialized   using   expression-list   as   the   initializer  (see
>     _dcl.init_).

> This shows that omitting the initializer at all causes default
> initialisation. To see what the member() form does, one has to look
> at dcl.init:

I think you should trust your instincts here, at least in part.  Take a look
at the syntax for some help:

Here is the member initialization list:
  ctor-initializer:
    : mem-initializer-list
...
And here is a single member initialization:
  mem-initializer:
    mem-initializer-id ( expression-list_opt ) // (opt as in optional)
...

Now, reread the rules you quoted above.  The first rule (the first "--")
defines that for cases like:
  ClassName::ClassName() : member() {}
member is default-initialized; the second rule defines that for cases like:
  ClassName::ClassName() : member(TypeOfMember()) {}
member is direct-initialized by the initializer "TypeOfMember()".  So far, the
case where a member's mem-initializer is omitted hasn't been examined.

>   8.5  Initializers                                           [dcl.init]

> [...]

> 7 An  object whose initializer is an empty set of parentheses, i.e., (),
>   shall be default-initialized.

> Since member() has those parentheses, it's a default initialisation
> as well.

Looks good to me.

> Finally, default initialisation is indeed zero-initialisation for PODs,
> as seen in 8.5/5:

>   To default-initialize an object of type T means:

>   --if  T is a non-POD class type (_class_), the default constructor for
>     T is called (and the initialization is ill-formed if T has no acces-
>     sible default constructor);

>   --if T is an array type, each element is default-initialized;

>   --otherwise, the storage for the object is zero-initialized.

> So it seems that - at least in CD2 - you cannot get an unintialized
> object member if you have an explicit constructor, and POD members
> are always zero-initialized if not explicitly initialized to
> something else.

> Therefore, the extra copy is indeed unnecessary (as is the complete
> initializer list, for that matter).

IS (and CD2) 12.6.2/4 states (among other things) that a non-static data
member of POD type "is not initialized."  I didn't quote the entire thing
here for carpal tunnel syndrome reasons.

> Except, of course, if this has changed since CD2 (has it?)

Nope.  I frankly think it makes sense the way it is (the initialization rules,
not the definition of class template pair).

-Chris Gurnee

--

mangled email: gurnec_at_mediaone_dot_net

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Michael McKibben" <mckibben@cerf.net>
Date: 1998/10/07
Raw View
I am trying to understand why the default constructor for std::pair uses
copy constructors for the template arguments instead of just their
respective default constructor. All implementations of std::pair that I have
seen look something like:

template<class T!, class T2> struct pair {
    // other details omitted...
    //

    // default constructor
    pair() : first(T1()), second(T2()) {}

    // to me, it seems that the default constructor should be implemented as
the following:
    // pair() : first(), second() {}
};

Why? It seems that this uses unnecessary temporary objects to construct each
of the template arguments, and also requires that the template arguments
define a public copy constructor. Since we can get away with using template
arguments that only define a copy constructor when constructing std::pair
objects, why can't we be allowed to get away with using template arguments
that only define a default constructor?

--mike <mckibben@cerf.net>




[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@localhost.localdomain.COM (Siemel Naran)
Date: 1998/10/08
Raw View
On 7 Oct 1998 15:05:51 GMT, Michael McKibben <mckibben@cerf.net> wrote:

>    // default constructor
>    pair() : first(T1()), second(T2()) {}

Necessary for cases where T1 and T2 are builtin types.  When you create
a builtin type which is not static or global, the default ctor is not
called.  The default ctor for a fundamental type initializes the value
to zero.  Suppose T1==int, T2==int

struct pair { ...;   pair() : first(), second() {}   };
int main() {
     pair p; // p.first and p.second are not set to 0

struct pair { ...;   pair(int()) : first(int()), second() {}   };
int main() {
     pair p; // p.first and p.second are set to 0


>    // to me, it seems that the default constructor should be implemented as
>the following:
>    // pair() : first(), second() {}
>};

To me, it seems it should be done this way too.  If only the default ctors
for the fundamental types would always be called.  I suppose they want
backward compatability with C.



>Why? It seems that this uses unnecessary temporary objects to construct each
>of the template arguments, and also requires that the template arguments
>define a public copy constructor. Since we can get away with using template
>arguments that only define a copy constructor when constructing std::pair
>objects, why can't we be allowed to get away with using template arguments
>that only define a default constructor?

Good point.  But most objects have a copy ctor anyway.  Don't worry about
the unnecessary temporary object -- it should be optimized away.  It's
sort of like the return value optimization.


--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Ron Natalie <ron@sensor.com>
Date: 1998/10/08
Raw View
Michael McKibben wrote:


>     // default constructor
>     pair() : first(T1()), second(T2()) {}
>
>     // to me, it seems that the default constructor should be implemented as
> the following:
>     // pair() : first(), second() {}
> };


THe SGI lib just has it as pair() { }



[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: gurnec@my-dejanews.com
Date: 1998/10/08
Raw View
In article <slrn6vd836.3i3.sbnaran@localhost.localdomain>,
  sbnaran@KILL.uiuc.edu wrote:
>
> On 7 Oct 1998 15:05:51 GMT, Michael McKibben <mckibben@cerf.net> wrote:
>
> >    // default constructor
> >    pair() : first(T1()), second(T2()) {}
>
> Necessary for cases where T1 and T2 are builtin types.  When you create
> a builtin type which is not static or global, the default ctor is not
> called.  The default ctor for a fundamental type initializes the value
> to zero.  Suppose T1==int, T2==int
>
> struct pair { ...;   pair() : first(), second() {}   };
> int main() {
>      pair p; // p.first and p.second are not set to 0

Not true, this notation does default-initialize the members first and second
no matter what their types.  "default-initialize" for a fundamental type
means zero-initializing it.  If the initialization list was all together left
out, then the members would be of indeterminate values.

> struct pair { ...;   pair(int()) : first(int()), second() {}   };
> int main() {
>      pair p; // p.first and p.second are set to 0
>
> >    // to me, it seems that the default constructor should be implemented as
> >the following:
> >    // pair() : first(), second() {}
> >};

<clip>

For one thing, the standard requires that the default ctor be implemented the
first way (or rather, "as if" it were implemented that way).  As you observed,
it requires that the copy ctor for the contained types be defined and
accessible, but I don't know why this was deemed desirable.

--

mangled email: gurnec_at_mediaone_dot_net

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]