Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Initialize to invalid value or no default constructor

87 views
Skip to first unread message

Florian Weimer

unread,
Sep 28, 2009, 6:59:08 PM9/28/09
to
I haven't been exposed to much C++ during the past few years, and
picking it up again for various reasons. One thing that puzzles me is
whether to define a default constructor for an object which puts the
object into some invalid-but-defined state, even if there is no
reasonable default state (think initializing a person's name to the
empty string). Or should I delete the default constructor, to force
client code to always properly initialize the object with a real
value?

For impure objects which affect external state (such as file handles),
the case is clear. Their destruction has an external effect, and may
fail. You shouldn't throw exceptions from a destructor, so client
code should call a close method (which might throw) before destroying
the object. After that, the object is in a closed state, and it makes
sense to define a default constructor which initializes new objects in
this state. No harm done because you need to deal with the closed
state anyway.

But for pure objects, the answer is not so obvious to me. Back in the
old days, you couldn't use most containers if you didn't have a
default constructor, but nowadays, you can wrap your type with
boost::optional and get a default constructor for free (at a
performance cost, of course). Anyway, I tried to follow this
approach, but I don't really like it. The boost::optional pops up in
all kind of strange places, including local variables for conditional
initialization, like:

boost::optional<Foo> foo;
if (bar) {
foo = make_foo();
} else {
reconstruct_foo();
foo = get_reconstructed_foo();
}

I could put the condition into a subprogram, but that's often not very
natural. (I haven't got lambdas/closures at my disposal yet, so there
are no syntactically lightweight local functions.) In addition, the
lack of a default constructor infects other objects through
composition. This means that it's necessary to write constructors
with longish argument lists if you happen to need a record-like
structure to group a few objects. Another alternative is to use
std::tuple to express the grouping, but then you lose the field names.
Or you can use boost::optional liberally---which is what I end up
doing most of the time, so much that I wonder whether it's better to
define an invalid state for those objects and use it to write a
default constructor.

What do you think? Have you experienced something similar? How did
you deal with it?

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Nick Hounsome

unread,
Sep 29, 2009, 1:56:53 AM9/29/09
to
On 28 Sep, 23:59, Florian Weimer <f...@deneb.enyo.de> wrote:
> I haven't been exposed to much C++ during the past few years, and
> picking it up again for various reasons. One thing that puzzles me is
> whether to define a default constructor for an object which puts the
> object into some invalid-but-defined state, even if there is no
> reasonable default state (think initializing a person's name to the
> empty string). Or should I delete the default constructor, to force
> client code to always properly initialize the object with a real
> value?

IMHO the objective is to define useful invariants that make the
description, implementation and testing of a class more logical and
simple.

At the level of a general Person class I don't see that forbidding ""
as a name achieves anything useful to either the implementation or a
client of the class and hence I don't see it as a good example of
invalid initialization.

> For impure objects which affect external state (such as file handles),
> the case is clear. Their destruction has an external effect, and may
> fail. You shouldn't throw exceptions from a destructor, so client
> code should call a close method (which might throw) before destroying
> the object. After that, the object is in a closed state, and it makes
> sense to define a default constructor which initializes new objects in
> this state. No harm done because you need to deal with the closed
> state anyway.
>
> But for pure objects, the answer is not so obvious to me. Back in the
> old days, you couldn't use most containers if you didn't have a
> default constructor,

I don't think that that is true.
What you always need is a copy constructor.
Containers use copy construction into uninintialized memory and so
don't require a default constructor in most cases.

> but nowadays, you can wrap your type with
> boost::optional and get a default constructor for free (at a
> performance cost, of course). Anyway, I tried to follow this
> approach, but I don't really like it. The boost::optional pops up in
> all kind of strange places, including local variables for conditional
> initialization, like:
>
> boost::optional<Foo> foo;
> if (bar) {
> foo = make_foo();
> } else {
> reconstruct_foo();
> foo = get_reconstructed_foo();
> }
>
> I could put the condition into a subprogram, but that's often not very
> natural. (I haven't got lambdas/closures at my disposal yet, so there
> are no syntactically lightweight local functions.) In addition, the
> lack of a default constructor infects other objects through
> composition. This means that it's necessary to write constructors
> with longish argument lists if you happen to need a record-like
> structure to group a few objects. Another alternative is to use
> std::tuple to express the grouping, but then you lose the field names.
> Or you can use boost::optional liberally---which is what I end up
> doing most of the time, so much that I wonder whether it's better to
> define an invalid state for those objects and use it to write a
> default constructor.

sticking to vanilla standards what about using auto_ptr<Foo> ?

> What do you think? Have you experienced something similar? How did
> you deal with it?

If you have to write tests for your classes and/or prove 100% coverage
you will quickly come to realize that invalid states are more trouble
than they are worth.

For example, with an iostream like class, almost every method has to
handle a closed file and that means twice as many code paths and twice
as many tests. For iostream it's worth it because there would
otherwise be a lot of ugly client code but for the sort of classes
that you are talking about it's not.

You should also see from this why I think that your example of an
empty Person::name should not be considered invalid - I would not
want to have to write a Person test to check for it but would rather
consider it an input validation issue.

Note that if you also had another unnecessary invariant such as
Person::age > 0 you might naively end up quadrupling your tests to get
100% code coverage. Avoid this by checking all invariants in a
CheckInvariants() method that throws such as:

inline void CheckInvariants()
{
#ifndef NDEBUG
if( invalid )
{
throw X();
}
else if (invalid2)
{
throw Y();
}
#endif
}

void
Person::F()
{
CheckInvariants();
// do stuff
}

This also shows another testing tip which is that people unreasonably
asking for 100% PATH coverage (100% CODE coverage can sometimes be
reasonable) don't tend to realize that there are two paths through F()
and no tool that I know of does so either.

Goran

unread,
Sep 29, 2009, 8:47:54 PM9/29/09
to
On Sep 29, 12:59 am, Florian Weimer <f...@deneb.enyo.de> wrote:
> I haven't been exposed to much C++ during the past few years, and
> picking it up again for various reasons. One thing that puzzles me is
> whether to define a default constructor for an object which puts the
> object into some invalid-but-defined state, even if there is no
> reasonable default state (think initializing a person's name to the
> empty string). Or should I delete the default constructor, to force
> client code to always properly initialize the object with a real
> value?

As said, person class doesn't seem to be a good example. I'd add:
first off, you'd need to define a non-empty constructor; second, in
it, you'd have to e.g. throw to forbid users to pass an empty string.
And what for? Meh.

That said... I personally value class invariants greatly, so I'd avoid
default constructor if it adds complexity to that. Second thing to
take into consideration is state transitions within a class. There
should be as little as possible of them. If having a default ctor
means that object is unusable until "initialized", I'd avoid it until
forced up the wall ;-).

Goran.

Neil Butterworth

unread,
Sep 29, 2009, 8:45:55 PM9/29/09
to
Florian Weimer wrote:

> I haven't been exposed to much C++ during the past few years, and
> picking it up again for various reasons. One thing that puzzles me is
> whether to define a default constructor for an object which puts the
> object into some invalid-but-defined state, even if there is no
> reasonable default state (think initializing a person's name to the
> empty string).

This is a horrible practice which causes all sorts of problems - just
about everywhere you use an object you have to test its state.

> Or should I delete the default constructor, to force
> client code to always properly initialize the object with a real
> value?

No need to "delete" the default constructor - if you provide any other
constructor, the default will not be synthesised.

> For impure objects which affect external state (such as file handles),
> the case is clear. Their destruction has an external effect, and may
> fail. You shouldn't throw exceptions from a destructor, so client
> code should call a close method (which might throw) before destroying
> the object. After that, the object is in a closed state, and it makes
> sense to define a default constructor which initializes new objects in
> this state. No harm done because you need to deal with the closed
> state anyway.

Well, you may think you have to do that, but it's not what the Standard
Library does. Standard library file streams are closed by their
destructors, and this is normally seen as a good thing. Anyway, if the
file won't close, what are you going to do?

> But for pure objects, the answer is not so obvious to me. Back in the
> old days, you couldn't use most containers if you didn't have a
> default constructor, but nowadays, you can wrap your type with
> boost::optional and get a default constructor for free

For the Standard Library containers at least, this is normally not
necessary - your class must typically be copy-constructible and
assignable, but need not be default constructible.

[snip]

> What do you think? Have you experienced something similar? How did
> you deal with it?

I think you are making something of a mountain out of a molehill - write
the classes in the most natural manner, use the standard containers, and
you should have few problems.

Neil Butterworth

eca

unread,
Sep 30, 2009, 6:35:57 AM9/30/09
to
On Sep 29, 12:59 am, Florian Weimer <f...@deneb.enyo.de> wrote:
> I haven't been exposed to much C++ during the past few years, and
> picking it up again for various reasons. One thing that puzzles me is
> whether to define a default constructor for an object which puts the
> object into some invalid-but-defined state, even if there is no
> reasonable default state (think initializing a person's name to the
> empty string). Or should I delete the default constructor, to force
> client code to always properly initialize the object with a real
> value?

IMHO, being valid or invalid is something bound to the semantics of
object data values, let's say it affers to the domain of the problem
being modelled in your program. I use to think of it as "high-level"
semantics, let me say.

Being defined or undefined is something bound to class invariants,
a sort of lower-level semantics. The program must behave consistently
and proficiently even if the high-level semantics of data are broken.
For example, somewhere in the program a sort of communication might be
given about the person's name being "invalid", however the program
flow
must go gently up to such point and possibly continue beyond it.

The two semantic levels should not be mixed. The default constructor
must, IMHO, construct a "defined" object (in the sense of low-level
semantics, i.e. class invariants) giving the most natural default
values
to object members, i.e. zero to numbers, empty to strings, and so on.

The high-level semantics of data values should then be checked where
necessary, possibly in contexts where semantic "validity" is required,
and semantically meaningful behaviour shold be provided, case by case.

Just my 2c,
eca

Florian Weimer

unread,
Sep 30, 2009, 6:56:32 AM9/30/09
to
* Goran:

> As said, person class doesn't seem to be a good example. I'd add:
> first off, you'd need to define a non-empty constructor; second, in
> it, you'd have to e.g. throw to forbid users to pass an empty string.
> And what for? Meh.

To clarify, I had a "name of a person" class on my mind. If that's
still controversial, consider a file name class. On most systems, the
empty string is not a valid file name.

I suppose it's a good idea to detect such violations as early as
possible, so that it's more likely that the manifestation of an error
(probably an exception) is as close as possible to the error's cause.

> That said... I personally value class invariants greatly, so I'd avoid
> default constructor if it adds complexity to that. Second thing to
> take into consideration is state transitions within a class. There
> should be as little as possible of them. If having a default ctor
> means that object is unusable until "initialized", I'd avoid it until
> forced up the wall ;-).

I've got a bit of trouble reconciling this with the first paragraph.
If the name is always used in a way which requires it to be non-empty,
an empty name is in an invalid state which needs to be checked
separately.

Nick Hounsome

unread,
Oct 1, 2009, 6:37:27 PM10/1/09
to
On 30 Sep, 11:56, Florian Weimer <f...@deneb.enyo.de> wrote:
> * Goran:
>
> > As said, person class doesn't seem to be a good example. I'd add:
> > first off, you'd need to define a non-empty constructor; second, in
> > it, you'd have to e.g. throw to forbid users to pass an empty string.
> > And what for? Meh.
>
> To clarify, I had a "name of a person" class on my mind. If that's
> still controversial, consider a file name class. On most systems, the
> empty string is not a valid file name.

"name of a person" class is pointless because it has no behaviour
beyond that of string.

I don't consider the file name class a good example either.
It's a good class - because it can be used to abstract file naming
across systems if well designed - but a restriction against an empty
file name is not much use on its own - there are infinitely many file
names that you wont be able to open for one reason or another so
singling out "" for special treatment doesn't buy you a sufficient
level of confidence in the usability of the filename to be worth the
trouble.

> I suppose it's a good idea to detect such violations as early as
> possible, so that it's more likely that the manifestation of an error
> (probably an exception) is as close as possible to the error's cause.

IMHO "As early as possible" is on input.
Returning to your file name example - Suppose that you want a file
name for saving. A name might be valid but refer to a read only file.
The only reasonable places to check that are on input or on open -
constraints on a filename class wont do it.

>
> > That said... I personally value class invariants greatly, so I'd avoid
> > default constructor if it adds complexity to that. Second thing to
> > take into consideration is state transitions within a class. There
> > should be as little as possible of them. If having a default ctor
> > means that object is unusable until "initialized", I'd avoid it until
> > forced up the wall ;-).
>
> I've got a bit of trouble reconciling this with the first paragraph.
> If the name is always used in a way which requires it to be non-empty,
> an empty name is in an invalid state which needs to be checked
> separately.
>

This is a confusion of requirements on the application and
requirements on the class.
Yes you could have a PersonName class that checked them but then it
should really be called MyApplicationPersonName since the constrainst
are particular to your current application.
If you're still not convinced then suppose that your app require names
to be in upper case - Where would you enforce that?
Another reason against is that you'll end up passing PersonName&
around a lot instead of string& and that will tend to make those
functions less reusable too.

[One of my biggest problems in porting stuff has often been the use of
classes or typedefs when string or const char* or int would do
perfectly well. This tends to force me to pull in other headers and
even libraries that I wouldn't otherwise need and the most annoying
thing is that the usual reason given is that it is to make it more
portable!]

John G Harris

unread,
Oct 2, 2009, 10:06:44 PM10/2/09
to
On Thu, 1 Oct 2009 at 16:37:27, in comp.lang.c++.moderated, Nick
Hounsome wrote:

<snip>


>"name of a person" class is pointless because it has no behaviour
>beyond that of string.

<snip>

Extract initials ?
Extract surname and initials ?
Move surname to front, followed by a comma ?

I suppose it depends on your definition of 'behaviour'.

John
--
John Harris

Florian Weimer

unread,
Oct 3, 2009, 7:23:17 PM10/3/09
to
* Neil Butterworth:

> Well, you may think you have to do that, but it's not what the Standard
> Library does. Standard library file streams are closed by their
> destructors, and this is normally seen as a good thing. Anyway, if the
> file won't close, what are you going to do?

For output streams, you need to check that the internal buffer of the
file object could be written to disk. Otherwise, your files are
silently truncated in case of a full disk. Not good. (Most standard
library destructors don't throw.)

>> What do you think? Have you experienced something similar? How did
>> you deal with it?
>
> I think you are making something of a mountain out of a molehill - write
> the classes in the most natural manner, use the standard containers, and
> you should have few problems.

I'm just looking at it and are somewhat saddened by the resulting lack
of conciseness. It's also more difficult to reuse objects, leading to
a performance loss. I understand that lack of invalid states makes
programs more robust. But given the overhead (both in terms of syntax
and run-time cost), I'm wondering how many people actually choose not
to define default destructors for classes with value semantics which
serve as basic building blocks and are used by other classes as
components.

Florian Weimer

unread,
Oct 3, 2009, 7:23:30 PM10/3/09
to
* Nick Hounsome:

> At the level of a general Person class I don't see that forbidding ""
> as a name achieves anything useful to either the implementation or a
> client of the class and hence I don't see it as a good example of
> invalid initialization.

I was considering a person name object. If you do something with
names that requires that they are non-empty, your concerns about
reasoning about state seem to apply.

>> But for pure objects, the answer is not so obvious to me. Back in the
>> old days, you couldn't use most containers if you didn't have a
>> default constructor,
>
> I don't think that that is true. What you always need is a copy
> constructor.

Okay, maybe I made some stupid decisions and ran into some corner
cases, and lib.default.con.req was a bit misleading (it doesn't cover
map's operator[]). So this isn't a problem after all.

> sticking to vanilla standards what about using auto_ptr<Foo> ?

unique_ptr<Foo> would be better. There's nothing against it, really,
it's just another level of indirection.

> If you have to write tests for your classes and/or prove 100%
> coverage you will quickly come to realize that invalid states are
> more trouble than they are worth.

I'm not doing that, and it's unrealistic that I ever will (especially
for error handling paths or paths leading to undefined behavior).

> You should also see from this why I think that your example of an
> empty Person::name should not be considered invalid - I would not
> want to have to write a Person test to check for it but would rather
> consider it an input validation issue.

How do you tell that a string has been input-validated? Wouldn't it
be nice if you could see that on the C++ type level?

> Note that if you also had another unnecessary invariant such as
> Person::age > 0 you might naively end up quadrupling your tests to get
> 100% code coverage.

Isn't it linear if you've got an Age type with a checking constructor
which converts from an integer?

Apart from that, age is not an attribute of a person. 8-) It's much
better to keep track of the birthday. And that's certainly a class
where a default constructor is somewhat questionable.

But if the classes for the person's name and the birthday lack default
destructors, both must be passed to the constructor of the person
class, and that's where things start to get annoying. It certainly
has got a bit of feel that I'm programming contrary to the language,
applying concepts from other sources which do not really fit.

Florian Weimer

unread,
Oct 3, 2009, 7:23:52 PM10/3/09
to
* Nick Hounsome:

> I don't consider the file name class a good example either.
> It's a good class - because it can be used to abstract file naming
> across systems if well designed - but a restriction against an empty
> file name is not much use on its own - there are infinitely many file
> names that you wont be able to open for one reason or another so
> singling out "" for special treatment doesn't buy you a sufficient
> level of confidence in the usability of the filename to be worth the
> trouble.

So your file name class would have a default constructor, initializing
the file name to the empty string?

How would that state behave in terms of file name construction (like
"create a new file name, using this file name as the directory in
which it is located", or "make one file name relative to another one,
if it is not absolute")?

> Returning to your file name example - Suppose that you want a file
> name for saving. A name might be valid but refer to a read only file.
> The only reasonable places to check that are on input or on open -
> constraints on a filename class wont do it.

Right. There are syntactic constraints that could be enforced,
though.

>> I've got a bit of trouble reconciling this with the first paragraph.
>> If the name is always used in a way which requires it to be non-empty,
>> an empty name is in an invalid state which needs to be checked
>> separately.
>>
>
> This is a confusion of requirements on the application and
> requirements on the class. Yes you could have a PersonName class
> that checked them but then it should really be called
> MyApplicationPersonName since the constrainst are particular to your
> current application.

I'm not writing frameworks, so all my classes refer to fairly concrete
concepts (in terms of the application). IMHO, abstractions makes only
sense when you've dealt with the concepts for a while and know that
they are sound.

> Another reason against is that you'll end up passing PersonName&
> around a lot instead of string& and that will tend to make those
> functions less reusable too.

That's a general argument against strong typing and abstract data
types, so I don't think it's convincing. (And there are templates...)

> [One of my biggest problems in porting stuff has often been the use of
> classes or typedefs when string or const char* or int would do
> perfectly well. This tends to force me to pull in other headers and
> even libraries that I wouldn't otherwise need and the most annoying
> thing is that the usual reason given is that it is to make it more
> portable!]

I've come across that, too, but that's usally a custom strings
library, used for all kinds of strings, and not actually an
application/library-specific class.

Neil Butterworth

unread,
Oct 4, 2009, 1:20:28 PM10/4/09
to
Florian Weimer wrote:

>> Well, you may think you have to do that, but it's not what the Standard
>> Library does. Standard library file streams are closed by their
>> destructors, and this is normally seen as a good thing. Anyway, if the
>> file won't close, what are you going to do?
>
> For output streams, you need to check that the internal buffer of the
> file object could be written to disk.

In over 20 years of C++ programming, I have never done this, and never
seen any code that does this. If your disk is full (how often does that
happen?) you simply say that the behaviour of your program is undefined.

>>> What do you think? Have you experienced something similar? How did
>>> you deal with it?
>> I think you are making something of a mountain out of a molehill - write
>> the classes in the most natural manner, use the standard containers, and
>> you should have few problems.
>
> I'm just looking at it and are somewhat saddened by the resulting lack
> of conciseness. It's also more difficult to reuse objects, leading to
> a performance loss. I understand that lack of invalid states makes
> programs more robust. But given the overhead (both in terms of syntax
> and run-time cost), I'm wondering how many people actually choose not
> to define default destructors for classes with value semantics which
> serve as basic building blocks and are used by other classes as
> components.

I never define a default constructor unless it makes sense for the
functionality of the class, which it rarely does. In the case of your
Person class, I would definitely not provide one. This doesn't cause me
any problems (I can happily live without std:;map's operator[]) and
actually fosters re-use, as the behaviour of the class is much more
transparent than if the user of it had to keep worrying about invalid
states.

Neil Butterworth

0 new messages