Re: RAII not always applicable?
on Mon Jun 01 2009, restor <akrzemi1-AT-interia.pl> wrote:
I actually didn't mean RAII, but a more general concept that could be
called "constructor establishes the class invariant".
The (nontrivial) constructor _always_ establishes the class invariant.
If you think it doesn't in some case, someone has mis-documented the
invariant. Believe me, I know what you mean, but I've been down that
road and have personally found that the only way to keep sane is to be
honest about what "invariant" means.
I wish it had a name as popular as RAII. Much as I find the concept
useful, I found at least two situations where it cannot be applied.
Then you're wishing for a stronger invariant than the one you actually
First situation is the std::istream interface for reading the data;
that is, the operator >>. If you want to read a piece of data from the
stream interface, you first have to create an object with a useless
value and then override that value with operator >>, like in the below
std::cin >> val;
Yeah, that's really annoying. That's a trivial constructor, though. If
it were a type with a nontrivial constructor, the invariant would be
If T was a typedef for int, the problem is not that big, but still
until we do the stream extraction, the value is indeterminate.
user defined type, the problem may be bigger, because I might not like
the default constructor at all, but I was forced to create it to be
able to use operator >>, or otherwise I would have been forced to
abandon operator >> for my type if I wanted a one-stage construction.
operator>> is not a good general choice for deserialization because
of this problem. That's why you'll see another option in the
Reading from the stream may not be the best way of initializing an
object (I don't really know), but I have seen it used in
boost::lexical_cast and in currently developed boost::convert. In
order for boost::convert to work with user types that do not support
default construction, the user is required to provide a value of that
type only to replace default construction requirement with copy
Second situation is when two objects need to be constructed and they
need to refer to one another. For illustration, the below is an
example of symmetric coroutines from boost::coroutine:
producer = producer_type( bind(
consumer = consumer_type( bind(
Here because producer coroutine needs a reference to consumer
coroutine and vice versa they have to be initialized in two stages.
Now, as to the purpose of this post, I was trying to identify classes
of problems where the pattern "constructor establishes the class
invariant" cannot be applied.
It can always be applied: you just need to weaken the ideal invariant to
correspond to reality.
I would appreciate any comment on if the problems above could have
been solved with one-stage initialization, but I failed to see how, or
if there are any other classes of problems that require a two-stage
I think that technically speaking, your desired producer/consumer
invariant can be established in one stage, but you'd need to have the
producer construct the consumer or vice-versa.
I guess the question you're really asking is: what sorts of invariants
can't be established in one stage? That's an interesting one, and I
don't have the answer :-)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]