Topic: C++0x Wish List (requires_all_initializers)


Author: allan_w@my-dejanews.com (Allan W)
Date: Wed, 16 Oct 2002 20:28:54 +0000 (UTC)
Raw View
> Ken Thomases <ken@spamnot.codeweavers.com> writes:
> > I would like some way to mark a class declaration so that the compiler
> > would force me to explicitly initialize all base classes and data
> > members in every constructor's initializer list.  For purposes of
> > discussion I picked the very tentative (and cumbersome) keyword name
> > requires_all_initializers.  It would be used like this:
> >
> > class Bar { ... };
> >
> > class Foo requires_all_initializers : public Bar
> > {
> > public:
> >      Foo();
> >      Foo(const Foo& other);
> >
> > private:
> >      int a;
> > };
...
> > Should there be an attempt to recognize pure interface base classes and
> > exclude them from the requirement to appear in the initializer list?
> > Any other candidates for exclusion?  The simple answer is no, don't
> > allow any exclusions.  Slightly more work, but avoids possible pitfalls.
> >   If the rules for exclusion aren't simple, programmers will be confused
> > and/or misled.

celtschk@web.de (Christopher Eltschka) wrote
> An obvious way would be to allow to omit initializers for classes
> where _only_ a default constructor exists.

What would you do with default constructors that take parameters?
    struct Foo {
        Foo(int x=1, int y=2);
    };

When inheriting from Foo, would you be required to initialize the
base class?

> > Also, consider:
> >
> > class Base { ... }; // Meets rule for exclusion
> >
> > class Derived requires_all_initializers : public Base
> > {
> > public:
> >      Derived() { }   // Doesn't init base, but legal
> > };
> >
> > Later, Base is changed in a manner that doesn't change the interface it
> > exposes to derived classes but it no longer meets the exclusion rules.
> > Now, Derived generates errors when it is recompiled.  What if they were
> > programmed by different people, departments, etc.
>
> Simple solution: Don't use this feature ;-)
>
> BTW, with the "only default constructor" criterion, the only way not
> to meet it any longer is to change the interface (by including a
> non-default constructor).

Or by changing a default constructor to have new or changed parameters
with defaults.

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: celtschk@web.de (Christopher Eltschka)
Date: Tue, 15 Oct 2002 18:41:47 +0000 (UTC)
Raw View
Ken Thomases <ken@spamnot.codeweavers.com> writes:

> I would like some way to mark a class declaration so that the compiler
> would force me to explicitly initialize all base classes and data
> members in every constructor's initializer list.  For purposes of
> discussion I picked the very tentative (and cumbersome) keyword name
> requires_all_initializers.  It would be used like this:
>
> class Bar { ... };
>
> class Foo requires_all_initializers : public Bar
> {
> public:
>      Foo();
>      Foo(const Foo& other);
>
> private:
>      int a;
> };
>
>
> The result would be that the following:
>
> Foo::Foo() : a(5)
> {
> }
>
> would generate an error because the base class object was not
> initialized in the initializer list.
>
> Similarly,
>
> Foo::Foo(const Foo& other) : Bar(other)
> {
>      a = other.a;
> }
>
> would generate an error because the data member a was not in the
> initializer list.  Note that I don't require/want the compiler to check
> the constructor body for code which assigns to 'a'.  (That would not
> generally be possible since calling someFunction(&a) might or might not
> assign through the pointer and change a.)  This is strictly a check for
> presence in the initializer list.
>
> Note that explicitly default-initializing a data member or subobject
> satisfies the requirement even for built-in types with a do-nothing
> default initializer.  So long as the programmer has been forced to
> explicitly make the choice.
>
>
> This should address the following problems:
>
> - When initially implementing the class, forgetting to initialize data
> members or base class subobjects.
>
> - When maintaining a class and adding a data member, forgetting to
> initialize it in one of the constructors.  This is essentially the same
> as the above; I mention it separately because it's much more likely to
> occur this way.
>
> - Unwittingly default-constructing an indirectly-inherited virtual base
> class by omission.

IMHO a virtual base class should either only have a default
constructor, or not have a default constructor at all (and the latter
case should be the exception). So this error shouldn't occur.

>
> - Self-documents.  Did the previous programmer intend for that data
> member to be default constructed, or did s/he forget?  Can't forget with
> requires_all_initializers.

And what if the default constructor is the only one? Why should I have
to state the obvious?

>
> It doesn't address these related problems:
>
> - Forgetting to assign to data members or base class subobjects when
> implementing assignment operators.  For copy assignment, the
> copy-construct-then-swap idiom helps, but see the next note.
>
> - Forgetting to swap data members or subobjects when implementing swap.
>
>
> Considerations:
>
> Should requires_all_initializers also force initializers to appear in
> the actual order of construction?  I'm strongly leaning toward yes.  I'm
> hard-pressed in general to come up with a reason for an initializer list
> to be out-of-order.  I understand several lint-like tools and compilers
> already warn about out-of-order initializer lists, so they seem to
> agree.  I'd be interested in the argument against this requirement if
> somebody wants to make it.

Enforcing the order of initializers seems a good thing to me, but I
don't think this warrants a language extension (I'd like to have
out-of-order initializer lists to be deprecated in the next version of
the standard, though)

>
>
> Should it be inherited?  On the one hand, the person who implements a
> class might not want requires_all_initializers despite inheriting from a
> base which uses it.  On the other hand, inheriting it would be a great
> boon to the use of virtual base classes.  A class designed for virtual
> inheritance, or a class which immediately virtually inherits, could
> apply the requires_all_initializers attribute and rest assured that no
> derived classes would omit the constructor for the virtual base.

Well, my opinion about virtual bases you already know.

It should definitely _not_ be inherited.

>
>
> Should there be an attempt to recognize pure interface base classes and
> exclude them from the requirement to appear in the initializer list?
> Any other candidates for exclusion?  The simple answer is no, don't
> allow any exclusions.  Slightly more work, but avoids possible pitfalls.
>   If the rules for exclusion aren't simple, programmers will be confused
> and/or misled.

An obvious way would be to allow to omit initializers for classes
where _only_ a default constructor exists.

>
> Also, consider:
>
> class Base { ... }; // Meets rule for exclusion
>
> class Derived requires_all_initializers : public Base
> {
> public:
>      Derived() { }   // Doesn't init base, but legal
> };
>
> Later, Base is changed in a manner that doesn't change the interface it
> exposes to derived classes but it no longer meets the exclusion rules.
> Now, Derived generates errors when it is recompiled.  What if they were
> programmed by different people, departments, etc.

Simple solution: Don't use this feature ;-)

BTW, with the "only default constructor" criterion, the only way not
to meet it any longer is to change the interface (by including a
non-default constructor).

Note that the very same problen arises if you make r_a_i
inherited. Even worse: Here you can get the problem without changing
_anything_ at the direct base class:

  class SomeHelper
  {
    ...
  };

  class Base:
    private SomeHelper
  {
    Base();
    Base(int);
    ...
  };

  class Derived:
    public Base
  {
    Derived() { ... } // implicit call of Base::Base()
  };

Now someone changes SomeHelper to

  class SomeHelper requires_all_initializers
  {
    ...
  };

and suddenly class Derived breaks.

[...]

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Allan_W@my-dejanews.com (Allan W)
Date: Tue, 3 Sep 2002 16:43:56 +0000 (UTC)
Raw View
Ken Thomases <ken@spamnot.codeweavers.com> wrote
> I suppose basically what it comes down to is that I prefer that all
> initializers always be present.  That's a coding style policy issue, I
> suppose, but I'd like the compiler's help with detecting violations.
> (This is a Wish List after all. :)
>
> There are plenty of features of C++ designed solely to help the
> programmer avoid errors.  Strong type-safety, for instance. ;)  Of
> course, my proposal isn't necessary to the language.  It's not really
> about adding power or expressibility, it's about giving me the choice to
> disable a feature I find counter-productive.  Similar to "explicit", but
> admittedly less urgent of a need.
>
> Allan W wrote:
> > If you can remember
> > to include the new keyword on every new class, why can't you just
> > remember to explicitly initialize all base classes and member variables?
>
> Because there are multiple occassions to make the choice (and hence
> forget/overlook something).  There may be multiple constructors to
> implement.  Data members may be added during class maintenance or
> enhancement.  The programmer who makes the choice to apply the keyword
> to the class may not be the one who implements, maintains, or enhances
> the constructors.  (This last stands in stark contrast to the "=="
> example, unless you favor a particularly frenzied style of "pair
> programming". :)
>
> Maybe I'm particularly insecure or paranoid, but don't you ever have
> that slight lingering unease that you missed something when you add a
> data member or base class?  The urge to triple-check?  Don't you feel
> even greater dread when you know a fellow programmer on the project
> _doesn't_ experience this twinge of anxiety?

I think you're right, that it's a coding style policy issue.

Perhaps it would be sufficient for your shop to implement a policy
that classes designed for inheritance do not have default constructors?

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Ken Thomases <ken@spamnot.codeweavers.com>
Date: 28 Aug 2002 08:45:11 GMT
Raw View
I would like some way to mark a class declaration so that the compiler
would force me to explicitly initialize all base classes and data
members in every constructor's initializer list.  For purposes of
discussion I picked the very tentative (and cumbersome) keyword name
requires_all_initializers.  It would be used like this:

class Bar { ... };

class Foo requires_all_initializers : public Bar
{
public:
     Foo();
     Foo(const Foo& other);

private:
     int a;
};


The result would be that the following:

Foo::Foo() : a(5)
{
}

would generate an error because the base class object was not
initialized in the initializer list.

Similarly,

Foo::Foo(const Foo& other) : Bar(other)
{
     a = other.a;
}

would generate an error because the data member a was not in the
initializer list.  Note that I don't require/want the compiler to check
the constructor body for code which assigns to 'a'.  (That would not
generally be possible since calling someFunction(&a) might or might not
assign through the pointer and change a.)  This is strictly a check for
presence in the initializer list.

Note that explicitly default-initializing a data member or subobject
satisfies the requirement even for built-in types with a do-nothing
default initializer.  So long as the programmer has been forced to
explicitly make the choice.


This should address the following problems:

- When initially implementing the class, forgetting to initialize data
members or base class subobjects.

- When maintaining a class and adding a data member, forgetting to
initialize it in one of the constructors.  This is essentially the same
as the above; I mention it separately because it's much more likely to
occur this way.

- Unwittingly default-constructing an indirectly-inherited virtual base
class by omission.

- Self-documents.  Did the previous programmer intend for that data
member to be default constructed, or did s/he forget?  Can't forget with
requires_all_initializers.

It doesn't address these related problems:

- Forgetting to assign to data members or base class subobjects when
implementing assignment operators.  For copy assignment, the
copy-construct-then-swap idiom helps, but see the next note.

- Forgetting to swap data members or subobjects when implementing swap.


Considerations:

Should requires_all_initializers also force initializers to appear in
the actual order of construction?  I'm strongly leaning toward yes.  I'm
hard-pressed in general to come up with a reason for an initializer list
to be out-of-order.  I understand several lint-like tools and compilers
already warn about out-of-order initializer lists, so they seem to
agree.  I'd be interested in the argument against this requirement if
somebody wants to make it.


Should it be inherited?  On the one hand, the person who implements a
class might not want requires_all_initializers despite inheriting from a
base which uses it.  On the other hand, inheriting it would be a great
boon to the use of virtual base classes.  A class designed for virtual
inheritance, or a class which immediately virtually inherits, could
apply the requires_all_initializers attribute and rest assured that no
derived classes would omit the constructor for the virtual base.


Should there be an attempt to recognize pure interface base classes and
exclude them from the requirement to appear in the initializer list?
Any other candidates for exclusion?  The simple answer is no, don't
allow any exclusions.  Slightly more work, but avoids possible pitfalls.
  If the rules for exclusion aren't simple, programmers will be confused
and/or misled.

Also, consider:

class Base { ... }; // Meets rule for exclusion

class Derived requires_all_initializers : public Base
{
public:
     Derived() { }   // Doesn't init base, but legal
};

Later, Base is changed in a manner that doesn't change the interface it
exposes to derived classes but it no longer meets the exclusion rules.
Now, Derived generates errors when it is recompiled.  What if they were
programmed by different people, departments, etc.


The name.  I couldn't settle on a really good way to specify this.  I'm
totally open to suggestions on this.  How should
requires_all_initializers actually be specified?

I considered making this an additional feature of my proposed 'explicit
class' idea (see
http://groups.google.com/groups?threadm=3D48C729.40503%40spamnot.codeweavers.com&rnum=1)
instead of a separate attribute.  The two are related (suppressing for a
class the implicit compiler-supplied behavior that masks programmer
omissions), but I'm not sure they're so closely related that I want them
to be tied together.

Would a programmer want 'explicit class' without
'requires_all_initializers'?  Possibly: explicit class is going to force
the programmer to implement more constructors manually.  With
requires_all_initializers, each constructor will require a potentially
long initializer list for the programmer to type.  In most cases though,
the programmer who wants the safety of explicit class probably also
wants the safety of requires_all_initializers.  After all, the
compiler-generated copy constructor and assignment operator are
guaranteed to hit every data member and subobject, if not necessarily
semantically correctly.  Choosing to forego the compiler-generated
version and implement it yourself is less stressful if the compiler will
help you get it right.  Also, for non-built-in types the initializer is
the proper, efficient place to initialize. (Well, duh! :-)  Further, a
class with a long initializer list is likely to be a class that needs to
be refactored.

Would a programmer want 'requires_all_initializers' without 'explicit
class'?  This seems more likely.  If the compiler-supplied functions are
fine, let them be.  Just check the initializers of the constructors that
I do implement.

This also impacts on the question of inheritance, above.  I definitely
think that 'explicit class' shouldn't be inherited -- it should be up to
each class's implementor if s/he wants compiler-generated functions in
her/his class.  (Of course, a base class that's explicit might not have
the requisite constructor/operator= thus inhibiting compiler-generation
in the derived class.  But that's a solved problem.)  On the other hand,
whether requires_all_initializers should be inherited is an open question.


What do people think?  I look forward to your comments.

                                         Ken

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Wed, 28 Aug 2002 07:32:22 CST
Raw View
In article <3D6C799B.5080003@spamnot.codeweavers.com>, Ken Thomases
<ken@spamnot.codeweavers.com> writes
>I would like some way to mark a class declaration so that the compiler
>would force me to explicitly initialize all base classes and data
>members in every constructor's initializer list.  For purposes of
>discussion I picked the very tentative (and cumbersome) keyword name
>requires_all_initializers.  It would be used like this:

Proposals should start with a clear motivation and a discussion of how
the objective can be attained at present.

I can see no motivation for this because this is a designer's issue and
is the task of code reviews etc. to enforce it. Base classes that do not
have default ctors have to be explicitly initialised in derived class
ctor init lists.

I believe that your proposal has very little benefit to the language at
the cost of added complexity. Persuade me I am wrong.


--
Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Allan_W@my-dejanews.com (Allan W)
Date: 28 Aug 2002 22:15:12 GMT
Raw View
Francis Glassborow <francis.glassborow@ntlworld.com> wrote
> Ken Thomases <ken@spamnot.codeweavers.com> writes
> >I would like some way to mark a class declaration so that the compiler
> >would force me to explicitly initialize all base classes and data
> >members in every constructor's initializer list.  For purposes of
> >discussion I picked the very tentative (and cumbersome) keyword name
> >requires_all_initializers.  It would be used like this:
>
> Proposals should start with a clear motivation and a discussion of how
> the objective can be attained at present.
>
> I can see no motivation for this because this is a designer's issue and
> is the task of code reviews etc. to enforce it. Base classes that do not
> have default ctors have to be explicitly initialised in derived class
> ctor init lists.

Code reviews catch things that the compiler does not. If the compiler
catches an error (or a probable error), overall this will reduce the
cost of software.

A seperate, but related question, is if this proposal would actually
do that. Maybe, but it would be rare.

Someone I know suggests that when comparing a literal to a variable,
the literal should always go on the left (3==i, never i==3) because
if you mistakenly use a single equals sign, the former is an error
while the latter usually compiles correctly and dies in user testing.
But if you can remember to put the literal first, why can't you also
remember to use two equals signs? If you do, the order doesn't matter.

Similar reasoning applies to this new keyword. If you can remember
to include the new keyword on every new class, why can't you just
remember to explicitly initialize all base classes and member variables?

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Ken Thomases <ken@spamnot.codeweavers.com>
Date: 29 Aug 2002 17:05:07 GMT
Raw View
I wrote:
 >I would like some way to mark a class declaration so that the compiler
 >would force me to explicitly initialize all base classes and data
 >members in every constructor's initializer list.  For purposes of
 >discussion I picked the very tentative (and cumbersome) keyword name
 >requires_all_initializers.  It would be used like this:


Francis Glassborow <francis.glassborow@ntlworld.com> wrote
 >Proposals should start with a clear motivation and a discussion of how
 >the objective can be attained at present.
 >
 >I can see no motivation for this because this is a designer's issue and
 >is the task of code reviews etc. to enforce it.

The motivation, as Allan W points out below, is to have the compiler
help with the task.  Programmers sometimes make errors; code reviews
sometimes miss them.  Currently, if a programmer omits a data member or
base class from the initializer list, the compiler "helpfully" supplies
the default constructor if accessible.  This may be what the programmer
intended, or the compiler may be masking a programmer error.  The second
programmer to come along may wonder whether the omission was intentional
and it may take some examination of the semantics of multiple classes to
satisfy him/herself.  If s/he has any compassion for the third
programmer, s/he will then add the appropriate initializer to the
initializer list.

I suppose basically what it comes down to is that I prefer that all
initializers always be present.  That's a coding style policy issue, I
suppose, but I'd like the compiler's help with detecting violations.
(This is a Wish List after all. :)  After all, it's the compiler that
otherwise masks the violation.  On a technical level, this is why I
don't see it as terribly complicating the language because it's just
stopping the compiler at the point where it supplies the
default-construction of items missing from the the initializer list and
causes an error to be emitted instead.  The compiler is already there
evaluating the completeness of the list, I'd just like to short-circuit
its helping behavior.

There are plenty of features of C++ designed solely to help the
programmer avoid errors.  Strong type-safety, for instance. ;)  Of
course, my proposal isn't necessary to the language.  It's not really
about adding power or expressibility, it's about giving me the choice to
disable a feature I find counter-productive.  Similar to "explicit", but
admittedly less urgent of a need.

 >Base classes that do not
 >have default ctors have to be explicitly initialised in derived class
 >ctor init lists.

And what of base classes with both default and non-default constructors?
  Or a constructor which qualifies as default because all of its
parameters have default values?  Should these be deemed inappropriate
for use as base classes because they are error-prone?  These are
currently untenable choices for virtual base classes, but only because
there's no way to prevent the compiler masking the failure to explicitly
construct base class subobjects.


Allan W wrote:
> Code reviews catch things that the compiler does not. If the compiler
> catches an error (or a probable error), overall this will reduce the
> cost of software.
>
> A seperate, but related question, is if this proposal would actually
> do that. Maybe, but it would be rare.
>
> Someone I know suggests that when comparing a literal to a variable,
> the literal should always go on the left (3==i, never i==3) because
> if you mistakenly use a single equals sign, the former is an error
> while the latter usually compiles correctly and dies in user testing.
> But if you can remember to put the literal first, why can't you also
> remember to use two equals signs? If you do, the order doesn't matter.

But you only make the choice of operand (comparand?) order at the same
time that you type the operator "==".

> Similar reasoning applies to this new keyword. If you can remember
> to include the new keyword on every new class, why can't you just
> remember to explicitly initialize all base classes and member variables?

Because there are multiple occassions to make the choice (and hence
forget/overlook something).  There may be multiple constructors to
implement.  Data members may be added during class maintenance or
enhancement.  The programmer who makes the choice to apply the keyword
to the class may not be the one who implements, maintains, or enhances
the constructors.  (This last stands in stark contrast to the "=="
example, unless you favor a particularly frenzied style of "pair
programming". :)

Maybe I'm particularly insecure or paranoid, but don't you ever have
that slight lingering unease that you missed something when you add a
data member or base class?  The urge to triple-check?  Don't you feel
even greater dread when you know a fellow programmer on the project
_doesn't_ experience this twinge of anxiety?

Thanks for the feedback,
Ken

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]