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

Initialization and trivial constructors

182 views
Skip to first unread message

nroberts

unread,
Apr 22, 2012, 2:37:06 AM4/22/12
to
Say you were given a coding policy that stated, "Only initialize
members with non-trivial constructors and destructors in your
constructor's initialization list." Assuming they mean what they are
saying, and understand the difference between trivial and non-trivial
constructors, could there be a reasonable explanation for this policy?

I consider myself well-versed in the language but I don't know
everything. I can't think of any reason for a policy like this. In
fact, it's the exact opposite of what I'd say. If I were to write a
policy about initialization lists it would say to include everything,
or to only omit members with non-trivial constructors.


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

Dave Harris

unread,
Apr 22, 2012, 7:20:13 AM4/22/12
to
{ Article reformatted; please limit your lines to 70 charaters -mod }

robert...@gmail.com (nroberts) wrote (abridged):
> Say you were given a coding policy that stated, "Only initialize
> members with non-trivial constructors and destructors in your
> constructor's initialization list." Assuming they mean what they
> are saying, and understand the difference between trivial and
> non-trivial constructors, could there be a reasonable explanation
> for this policy?

They may be trying to say that:

Class::Class() : a(0), b(0), c(0), d(0) {
}

would be better written as:

Class::Class() {
a = 0;
b = 0;
c = 0;
d = 0;
}

The former may be more efficient if the members have code in their
constructors, but if they are just ints, then the efficiency will be
about the same. The latter version is arguably easier to read, write
and edit.

It matters more when the initialisation is more complex. For example,
the latter version makes the order of evaluation apparent in the code;
in the former version the order is determined by the class definition,
which may be in a different source file. If you need some code to
calculate the initial values, in the latter version you can just put
it on the previous line, and add whatever intermediate variables or
function calls you need. In the former version you have to write a
separate function to return the initial value you want, and either
pass lots of arguments to it, or else rely on the order of
initialisation which as I've already noted is fraught. Let's not even
think that we might need different exception handlers. Previous to
C++11, there were things that couldn't be initialised by initialiser
lists at all.

Basically, assignment statements are simple. Everyone understands
them. Initialisation lists are weird and full of restrictions and
special cases; they are part of what makes C++ hard. Sometimes you
need them for performance, though.

-- Dave Harris, Nottingham, UK.

Francis Glassborow

unread,
Apr 22, 2012, 9:30:44 AM4/22/12
to
You maybe right as to the motivation of the writers of the coding
policy, however I think those reasons are competely inadequate. They
should be asked to provide a rationale because without it a programmer
will not know when to vary from the policy.

I much prefer to see a policy that requires that everything be
initialised in the initialisation list unless it is impossible to do
so.


As to the order of initialisation problem, anyone who writes the
definition of a constructor without having the definition of the class
in front of them should be given a final warning for dangerous coding
habits.

Note that const and reference members have to be initialised in the
initialisation list.

Using assignment within the body of a constructor for a data member
should be a red flag that that member cannot be initialised.

One reason that some of us campaigned so hard to maximise the number
of things that could be initialised in initialisation lists was
exactly because we believed that the language should no be preventing
the programmer from using a good consistent style.


The solution to bad coding isn't rules but training. And it certainly
isn't rules that inhibit good programming style.

Francis

Wil Evers

unread,
Apr 22, 2012, 12:17:25 PM4/22/12
to
Dave Harris wrote:

> They may be trying to say that:
>
> Class::Class() : a(0), b(0), c(0), d(0) {
> }
>
> would be better written as:
>
> Class::Class() {
> a = 0;
> b = 0;
> c = 0;
> d = 0;
> }
>
> The former may be more efficient if the members have code in their
> constructors, but if they are just ints, then the efficiency will be
> about the same. The latter version is arguably easier to read, write
> and edit.

I would say an initializer list better reflects the way C++ handles
(sub-)object initialization. Of course, if a, b, c and d are just
ints with no dependency relationships, it doesn't really matter.
However, for a class that uses more complex, user-defined member
types, proper initialization before the constructor body is entered
often does matter, especially when one member variable somehow depends
on another.

> It matters more when the initialisation is more complex. For
> example, the latter version makes the order of evaluation apparent
> in the code; in the former version the order is determined by the
> class definition, which may be in a different source file.

A decent compiler will issue a warning if the order in the initializer
list doesn't match the order in the class definition.

> If you need some code to calculate the initial values, in the latter
> version you can just put it on the previous line, and add whatever
> intermediate variables or function calls you need. In the former
> version you have to write a separate function to return the initial
> value you want, and either pass lots of arguments to it, or else
> rely on the order of initialisation which as I've already noted is
> fraught.

In many cases, the initializer list is enough, and there's no need for
any code in the constructor body at all. Where I do need a body, I
often find that it is easier to understand if it can rely on
reasonably initialized member variables and base sub-objects.

Let's not forget that reading an uninitialized member variable
triggers undefined behavior; by always initializing them first, we at
least document what we expect to find there.

> Let's not even think that we might need different exception
> handlers.

Yes, that's a hard one. To me, it usually serves as red flag telling
me I need to re-evaluate my design.

> Previous to C++11, there were things that couldn't be initialised by
> initialiser lists at all.

I suppose you're referring to built-in arrays?

> Basically, assignment statements are simple. Everyone understands
> them. Initialisation lists are weird and full of restrictions and
> special cases; they are part of what makes C++ hard. Sometimes you
> need them for performance, though.

I would say initialization in the constructor body (instead of the
initializer list) makes more sense in languages that guarantee all
member variables have some pre-defined value we can count on.

- Wil

Martin B.

unread,
Apr 22, 2012, 3:38:35 PM4/22/12
to
On 22.04.2012 13:20, Dave Harris wrote:
> robert...@gmail.com (nroberts) wrote (abridged):
>> Say you were given a coding policy that stated, "Only initialize
>> members with non-trivial constructors and destructors in your
>> constructor's initialization list." (...)
>
> They may be trying to say that:
> (...)
>
> Basically, assignment statements are simple. Everyone understands
> them. Initialisation lists are weird and full of restrictions and
> special cases; they are part of what makes C++ hard. Sometimes you
> need them for performance, though.
>

I'll answer to this in semi-rant mode, as it seem to be a common theme
with C++. (It certainly is common where I work, also when discussing
with some friends.):

And I can't hear it anymore! C++ is a professional tool, and we should
use the language to it's full potential, and not limit ourselves to
what "everybody understands". The *whole* dang language is "weird and
full of restrictions", and still there we are using it, because one or
the other reason!

I mean, who's this Everybody anyway? Is it Alice the Apprentice? Is it
Never Change my C-with-classes Ways Noah? Is it Last Programmed C++
Years Ago Larry? None of these should have any business dictating how
Daily C++ Programmer Donald has to write his code.

cheers,
Martin

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.

nroberts

unread,
Apr 22, 2012, 3:55:08 PM4/22/12
to
On Apr 22, 9:17 am, Wil Evers <boun...@dev.null> wrote:
> Dave Harris wrote:
> > They may be trying to say that:
>
> > Class::Class() : a(0), b(0), c(0), d(0) {
> > }
>
> > would be better written as:
>
> > Class::Class() {
> > a = 0;
> > b = 0;
> > c = 0;
> > d = 0;
> > }
>
> > The former may be more efficient if the members have code in their
> > constructors, but if they are just ints, then the efficiency will be
> > about the same. The latter version is arguably easier to read, write
> > and edit.
>
> I would say an initializer list better reflects the way C++ handles
> (sub-)object initialization. Of course, if a, b, c and d are just
> ints with no dependency relationships, it doesn't really matter.
> However, for a class that uses more complex, user-defined member
> types, proper initialization before the constructor body is entered
> often does matter, especially when one member variable somehow depends
> on another.

What's interesting to me on this is that non-trivial, default
constructors are called by initialization whether you do so explicitly
or not. The trivial ones on the other hand, they are not.

Now, I am one to initialize everything in the constructor (when I
don't forget and miss something) anyway so I don't have to think about
it.

What rather upsets me about this policy, and I do believe it's being
made policy for the reason that Mr. Harris suggests, is that knowing
whether something has a trivial constructor is not as obvious as
asking, "Is it an int?" Many classes have trivial constructors, but
not all that have implicitly defined, default constructors do. So
finding out whether something's compiler generated constructor is
trivial or not is not itself going to be a trivial undertaking. It
requires more advanced knowledge of the language, I think, than
knowing how initializers work (though that does seem to be an issue
here).

Oh well. You've got to do what you've got to do. I just wanted to
see if there was something I'm missing because these are the experts
with hardened decades of coding behind them.

Francis Glassborow

unread,
Apr 22, 2012, 8:16:53 PM4/22/12
to
And the policy we are discussing introduces an unnecessary fragility
into the design because the redesign of a class may move it from one
with a trivial ctor to one without one.

Dave Abrahams

unread,
Apr 23, 2012, 12:18:10 PM4/23/12
to
on Sun Apr 22 2012, brangdon-AT-cix.compulink.co.uk (Dave Harris) wrote:

> { Article reformatted; please limit your lines to 70 charaters -mod }
>
> robert...@gmail.com (nroberts) wrote (abridged):
>> Say you were given a coding policy that stated, "Only initialize
>> members with non-trivial constructors and destructors in your
>> constructor's initialization list." Assuming they mean what they
>> are saying, and understand the difference between trivial and
>> non-trivial constructors, could there be a reasonable explanation
>> for this policy?
>
> They may be trying to say that:
>
> Class::Class() : a(0), b(0), c(0), d(0) {
> }
>
> would be better written as:
>
> Class::Class() {
> a = 0;
> b = 0;
> c = 0;
> d = 0;
> }
>
> The former may be more efficient if the members have code in their
> constructors, but if they are just ints, then the efficiency will be
> about the same. The latter version is arguably easier to read, write
> and edit.

I think Dave H. is being overly generous to the designers of such a
policy. IMO the disadvantages of putting things in the constructor body
that could go in the initialization list outweigh the advantages by a
very significant margin.

As soon as you put something in the constructor body, you've introduced
mutation, which makes code much harder to reason about. Objects
constructed in the initializer list don't exist until they're
initialized, so there is no mutation. You've also introduced semantics
that can't be automatically reversed if there's an exception.

Like the use of "hungarian notation", the policy makes the details of
code sensitive to the specific types used, rather than to the concepts
they model---should I really have to rewrite constructors if I
replace

typedef double fraction;

with

typedef boost::rational<long> fraction;

?

These are just the first three things that pop to mind; I'm sure I could
go on.

> It matters more when the initialisation is more complex. For example,
> the latter version makes the order of evaluation apparent in the code;
> in the former version the order is determined by the class definition,
> which may be in a different source file.

And about which most compilers can warn you if you don't match up the
order.

> If you need some code to calculate the initial values, in the latter
> version you can just put it on the previous line, and add whatever
> intermediate variables or function calls you need. In the former
> version you have to write a separate function to return the initial
> value you want, and either pass lots of arguments to it, or else rely
> on the order of initialisation which as I've already noted is
> fraught. Let's not even think that we might need different exception
> handlers.

Yes, let's not! Seriously, how often does that happen?

> Previous to C++11, there were things that couldn't be initialised by
> initialiser lists at all.
>
> Basically, assignment statements are simple. Everyone understands
> them. Initialisation lists are weird

They're weird if you treat them that way, i.e. by not getting in the
habit of using them everywhere.

> and full of restrictions and special cases;

A list, please! I've almost never felt limited by what I could do in
initialization lists... and, special cases? Really?

> they are part of what makes C++ hard.

Au contraire, they are part of what make C++ easy, if you know how to
use them and think about them. This is like saying "virtual functions
or exceptions are part of what make C++ hard." If they look mysterious
to you, you'll tend to avoid them, they'll remain mysterious, and you'll
always be fighting the language. If you embrace them and learn to
understand the abstractions they offer, they'll make everything easier.

> Sometimes you need them for performance, though.

Or, from my point of view: *very* occasionally you'll need to put code
in the constructor body, but the more you limit those occasions, the
easier your code will be to read, write, maintain, and understand.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Dave Harris

unread,
Apr 23, 2012, 5:36:05 PM4/23/12
to
robert...@gmail.com (nroberts) wrote (abridged):
> Now, I am one to initialize everything in the constructor (when I
> don't forget and miss something) anyway so I don't have to think
> about it.

I don't see the policy as being against that. I thought this was about whether
members are initialised in the constructor's initialiser list, or in the
constructor's body. Either way members should be initialised before the constructor
terminates.


> What rather upsets me about this policy, and I do believe it's being
> made policy for the reason that Mr. Harris suggests, is that knowing
> whether something has a trivial constructor is not as obvious as
> asking, "Is it an int?"

I think the policy is over-stated as written. I suspect the intent is more like,
"Use constructor initialiser lists if there is a good reason to, otherwise don't
worry about it." One of the problems with writing a coding policy is they bring out
the lawyer in us. One feels one has to find a form of words that expresses exactly
what a "good reason" is. Mentioning a non-trivial constructor was a valiant
attempt.

(Reading a coding policy can also bring out the lawyer in us.)

-- Dave Harris, Nottingham, UK.


Dave Harris

unread,
Apr 23, 2012, 5:38:48 PM4/23/12
to
da...@boostpro.com (Dave Abrahams) wrote (abridged):
> > They may be trying to say that:
> >
> > Class::Class() : a(0), b(0), c(0), d(0) {
> > }
> >
> > would be better written as:
> >
> > Class::Class() {
> > a = 0;
> > b = 0;
> > c = 0;
> > d = 0;
> > }
> >
> > The former may be more efficient if the members have code in their
> > constructors, but if they are just ints, then the efficiency will
> > be about the same. The latter version is arguably easier to read,
> > write and edit.
>
> As soon as you put something in the constructor body, you've
> introduced mutation, which makes code much harder to reason about.

Although that's a consideration, I don't think it's significant here. Obviously if
the members are const then we have to use an initialiser list. If we have the
option of using the constructor body, some mutation is inevitable.

It's a purely local matter, that only affects the few lines of code in the
constructor, which should be easy to reason about regardless. If the code does get
more complex, it will probably be too complex for initialiser lists.


> [...] You've also introduced semantics that can't be automatically
> reversed if there's an exception.

I don't follow this point. In the second example, how and why would you want to
reverse something like "a = 0;" when a is an int? Doesn't this issue only apply to
members whose constructor isn't trivial? If the constructor does nothing,
presumably the destructor does nothing too, else the class is probably unsafe
regardless.


> Like the use of "hungarian notation", the policy makes the details
> of code sensitive to the specific types used, rather than to the
> concepts they model---should I really have to rewrite constructors
> if I replace
>
> typedef double fraction;
>
> with
>
> typedef boost::rational<long> fraction;
>
> ?

I agree the policy is over-stated as written. It shouldn't require code to be
rewritten.

Note the code will still work with the same semantics after such a change. There
will at most be a performance difference, but a good compiler will probably
optimise it away, and if not, the difference will probably be too small to measure.
Measure it, then optimise if the overhead is significant.

I've more often written constructors using initialiser lists and then later
unpicked it to use the constructor body, than vice versa.


> > and full of restrictions and special cases;
>
> A list, please!

I've already mentioned them, but if you want it in list form:
o Order of evaluation may not be order written.
o Unable to add more code between initialisations (so it
doesn't scale).
o Unable to initialise arrays this way (prior to C++11).
o Exception handling has some weird syntax that I can't even
find online examples of.
o It's a different way of writing code.

By that last point, I mean the usual way of writing code is with statements, one
per line, terminated by a semi-colon. With initialiser lists we have instead
expressions separated by commas, that tend to all be put on the same line until it
doesn't fit and then it gets indented randomly. I've even seen people put the comma
at the start of the line. It's not just cosmetic. It means the code in the copy
constructor looks different to that in the assignment operator, or indeed any other
method.


> > they are part of what makes C++ hard.
>
> Au contraire, they are part of what make C++ easy, if you know how
> to use them and think about them.

I know how to use them and think about them. I am to some extent playing Devil's
Advocate here. Nevertheless, the points I'm making are valid.

-- Dave Harris, Nottingham, UK.


--

Dave Harris

unread,
Apr 23, 2012, 5:39:48 PM4/23/12
to
0xCDC...@gmx.at (Martin B.) wrote (abridged):
> > Basically, assignment statements are simple. Everyone understands
> > them. Initialisation lists are weird and full of restrictions and
> > special cases; they are part of what makes C++ hard. Sometimes you
> > need them for performance, though.
>
> I'll answer to this in semi-rant mode, as it seem to be a common
> theme with C++. (It certainly is common where I work, also when
> discussing with some friends.):
>
> And I can't hear it anymore! C++ is a professional tool, and we
> should use the language to it's full potential, and not limit
> ourselves to what "everybody understands". The *whole* dang language
> is "weird and full of restrictions", and still there we are using
> it, because one or the other reason!

I think this is missing the point. There is no benefit to making code needlessly
complicated. Prefer simplicity. The issue here is, which is simpler? I've mentioned
ways in which constructor initialise lists are more complicated. Are those
complications worth it?

In some cases they definitely are. For example, the member variable may benefit
from having a strong class invariant that requires it not have a default
constructor at all. Fine. In that case it's worth it. If it lets us use a const
member or a reference, that's probably worth it too. Sometimes performance reasons
will justify it.

Given such cases exist, there is an argument that initialiser lists always be used
for consistency. I don't think that's a very strong argument. I don't think the
other arguments I've seen so far are very strong either.

-- Dave Harris, Nottingham, UK.


--

Gene Bushuyev

unread,
Apr 24, 2012, 12:18:04 AM4/24/12
to
On Apr 23, 2:38 pm, brang...@cix.compulink.co.uk (Dave Harris) wrote:
> d...@boostpro.com (Dave Abrahams) wrote (abridged):
>
> > > and full of restrictions and special cases;
>
> > A list, please!
>
> I've already mentioned them, but if you want it in list form:
> o Order of evaluation may not be order written.

Can't agree it's a good reason, because 1) it's easy, and after a
little while it becomes a habit, so I would expect every experienced
programmer do it automatically; 2) order is usually unimportant; and
3) every compiler I know warns about it anyway.

> o Unable to add more code between initialisations (so it
> doesn't scale).

Of course you can add as complex initialization as you need, e.g.

A::A() : member(very_complex_initialization_function()) {}

You wouldn't want a put a long piece of code in constructor body
anyway. There isn't any difference in "scaling."

> o Unable to initialise arrays this way (prior to C++11).

Yes, but in the constructor body you don't initialize anything
(including arrays), you assign to already initialized object.

> o Exception handling has some weird syntax that I can't even
> find online examples of.

You probably meant function-try block. It might be syntactically
unsavory, but fortunately it's completely unnecessary, the only use
case I can think of is exception translation. If you can't construct
an object let the caller know that by throwing an exception. Don't
catch exceptions in the constructor and let them propagate to the
caller. You can't usually handle an exception in constructor anyway,
I've seen people creating zombie objects which only leads to fragile
unmaintainable code.

> o It's a different way of writing code.

I will skip this one, because in my view it's not an argument.

Miles Bader

unread,
Apr 24, 2012, 12:22:27 AM4/24/12
to
[I think a coding standard that _mandates_ avoiding initializers is
pretty dopey , but:]

bran...@cix.compulink.co.uk (Dave Harris) writes:
>> > and full of restrictions and special cases;
>>
>> A list, please!
>
> I've already mentioned them, but if you want it in list form:
> o Order of evaluation may not be order written

(but this is an easy and safe [no false positives] compiler warning)

> o Unable to add more code between initialisations (so it doesn't
> scale).

This is absolutely my main complaint about initializer lists -- I find
it a very common case that I want to calculate an intermediate value
based on the constructor arguments, and then use that value to
initialize several data members. The initializer-list form makes this
quite annoying.

Of course, if efficiency isn't an issue, once can just repeat the
calculations for each of the data members (probably calling a function
to avoid duplicating the actual code), but sometimes efficiency _is_
important... (if you're lucky, maybe the compiler can see what's going
on and optimize away the redundancy ... but maybe it can't....)

[Adding "intermediate code" to initialize a _single_ data-member isn't
that hard, because one can just use private static member functions
that return the initialization value.]

> o It's a different way of writing code.

I suppose, but that seems a pretty pointless argument in this context:
you basically _can't_ avoid initializers in any sane C++ code base, so
the devs are going to be using them a lot regardless -- and once
you're using them, you may as well use them always, because that
_reduces_ the variance (I'd say that initializers can be used pretty
painlessly like 98% of the time).

A coding standard that arbitrarily forbids using initializers for
cases X, Y, and Z, is just adding churn, making the code less
consistent and harder to understand.

-miles

--
Circus, n. A place where horses, ponies and elephants are permitted to
see men, women and children acting the fool.

Martin B.

unread,
Apr 24, 2012, 4:06:08 AM4/24/12
to
On 23.04.2012 23:39, Dave Harris wrote:
> 0xCDC...@gmx.at (Martin B.) wrote (abridged):
>>> Basically, assignment statements are simple. Everyone understands
>>> them. Initialisation lists are weird and full of restrictions and
>>> special cases; they are part of what makes C++ hard. Sometimes you
>>> need them for performance, though.
>>
>> I'll answer to this in semi-rant mode, as it seem to be a common
>> theme with C++. (It certainly is common where I work, also when
>> discussing with some friends.):
>>
>> And I can't hear it anymore! C++ is a professional tool, and we
>> should use the language to it's full potential, and not limit
>> ourselves to what "everybody understands". The *whole* dang language
>> is "weird and full of restrictions", and still there we are using
>> it, because one or the other reason!
>
> I think this is missing the point. There is no benefit to making code needlessly
> complicated. Prefer simplicity. The issue here is, which is simpler? I've mentioned
> ways in which constructor initialise lists are more complicated. Are those
> complications worth it?
>

There are some things needlessly complicated, but surely, initializer
lists are *not* among them.

Initializer lists are only "more" complicated -- if at all -- for some
special cases of initialization. (That were mentioned.) Then again, you
must use them in some other cases anyway.

So, I agree that *the existence* of initializer lists in the C++
language is a (syntactic) complication.

Once they exist, they are equivalently complex (to read and write) than
constructor initialization code - and the devs have to know them anyway,
so, as others argued, I guess *using them* when possible will lead to
*more consistent* code.

cheers,
Martin

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.


Martin B.

unread,
Apr 24, 2012, 4:08:03 AM4/24/12
to
On 24.04.2012 06:22, Miles Bader wrote:
> bran...@cix.compulink.co.uk (Dave Harris) writes:
>> o Unable to add more code between initialisations (so it doesn't
>> scale).
>
> This is absolutely my main complaint about initializer lists -- I find
> it a very common case that I want to calculate an intermediate value
> based on the constructor arguments, and then use that value to
> initialize several data members. The initializer-list form makes this
> quite annoying.
> (...)
> you basically _can't_ avoid initializers in any sane C++ code base, so
> the devs are going to be using them a lot regardless -- and once
> you're using them, you may as well use them always, because that
> _reduces_ the variance (I'd say that initializers can be used pretty
> painlessly like 98% of the time).
>

Hmmm ... Miles - you write "I find it a very common case (...)" and then
you write "painlessly like 98% of the time".

So, is 2% a common case? ;-)

It would be very interesting to get a real number (from you or
elsewhere) of how many constructors written really need to calculate
itermediate values for multiple member vars.

cheers,
Martin

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.


Tobias Müller

unread,
Apr 24, 2012, 4:08:47 AM4/24/12
to
Dave Harris <bran...@cix.compulink.co.uk> wrote:
> { Article reformatted; please limit your lines to 70 charaters -mod }
>
> robert...@gmail.com (nroberts) wrote (abridged):
>> Say you were given a coding policy that stated, "Only initialize
>> members with non-trivial constructors and destructors in your
>> constructor's initialization list." Assuming they mean what they
>> are saying, and understand the difference between trivial and
>> non-trivial constructors, could there be a reasonable explanation
>> for this policy?
>
> They may be trying to say that:
>
> Class::Class() : a(0), b(0), c(0), d(0) {
> }
>
> would be better written as:
>
> Class::Class() {
> a = 0;
> b = 0;
> c = 0;
> d = 0;
> }
>
> The former may be more efficient if the members have code in their
> constructors, but if they are just ints, then the efficiency will be
> about the same. The latter version is arguably easier to read, write
> and edit.

I usually write it like that:

Class::Class()
: a(0)
, b(0)
, c(0)
, d(0)
{ }

or:

Class::Class() :
a(0),
b(0),
c(0),
d(0)
{ }

which is both quite readable and easy to edit IMO.

BTW, would it be legal to add an additional comma at the end of my second
example after the 'b(0)'?

In my experience, people that avoid initializer lists also tend to omit
meaningful constuctors and use a combination of default constuctor and
custom initialization method, leaving the object in an invalid state
between those two.
Like:

Class::Class()
/* implicit:
: a()
, b()
, c()
, d()
*/
{
// a, b, c, d are in an invalid state
a.init(0);
b.create(0);
c.initialize(0);
d.init(0);
}

Tobi

Miles Bader

unread,
Apr 24, 2012, 12:55:56 PM4/24/12
to
"Martin B." <0xCDC...@gmx.at> writes:
>> you basically _can't_ avoid initializers in any sane C++ code base, so
>> the devs are going to be using them a lot regardless -- and once
>> you're using them, you may as well use them always, because that
>> _reduces_ the variance (I'd say that initializers can be used pretty
>> painlessly like 98% of the time).
>>
>
> Hmmm ... Miles - you write "I find it a very common case (...)" and then
> you write "painlessly like 98% of the time".
>
> So, is 2% a common case? ;-)

Good point, that was awkwardly put.

Trying to be more clear:

My experience is that most initializers are pretty simple, though some
might need to call a private static method to do more complicated
calculations. The case I mentioned above where one really wants to
share an intermediate calculation between initializers _is_ somewhat
rare, I think, but not vanishingly rare; it does show up from time to
time.

BTW, the C++11 "Delegating Constructor" feature might be a reasonable
workaround for this case -- one can add additional private
constructors that take the "intermediate values" as extra constructor
arguments.

-miles

--
Any man who is a triangle, has thee right, when in Cartesian Space,
to have angles, which when summed, come to know more, nor no less,
than nine score degrees, should he so wish. [TEMPLE OV THEE LEMUR]

nroberts

unread,
Apr 24, 2012, 7:08:58 PM4/24/12
to
On Apr 23, 9:18 pm, Gene Bushuyev <publicfil...@gbresearch.com> wrote:
> On Apr 23, 2:38 pm, brang...@cix.compulink.co.uk (Dave Harris) wrote:
>
>
> > o Unable to add more code between initialisations (so it
> > doesn't scale).
>
> Of course you can add as complex initialization as you need, e.g.
>
> A::A() : member(very_complex_initialization_function()) {}
>

And I think this monstrosity would work in C++11:

A::A(int x)
: member_a([x](){ lots of stuff and a return}())
, member_b([](type_a const& var){ stuff with member_a....and return}
(member_a))
{}

I'd probably slap someone around for doing that unnecessarily though.

nroberts

unread,
Apr 24, 2012, 7:15:56 PM4/24/12
to
On Apr 23, 2:36 pm, brang...@cix.compulink.co.uk (Dave Harris) wrote:
> roberts.n...@gmail.com (nroberts) wrote (abridged):
>
> > Now, I am one to initialize everything in the constructor (when I
> > don't forget and miss something) anyway so I don't have to think
> > about it.
>
> I don't see the policy as being against that. I thought this was
> about whether members are initialised in the constructor's
> initialiser list, or in the constructor's body. Either way members
> should be initialised before the constructor terminates.
>

I think it is against that, actually. Preferring to initialize in the
constructor body requires that you know whether or not there's good
reason to do it in the initialization list instead. This would be any
time that the constructor is:

1. not default
2. non-trivial.

Take both of these together and you might seem to have a viable policy
here that you can simply forget about. This isn't so however because
default, non-trivial constructed objects can be ommitted and they'll
initialize by default constructor. The trivial ones though will stay
in undefined state, and so you have to remember to initialize them in
the constructor body or give up on the idea of initializing
everything.

If you take out the first one and say that you should initialize
anything that is initialized by non-body code in the initializer, so
that everyone can see that it was, now you have to know the
difference.

In either of these cases you also have to watch for when classes that
have trivial constructors change to have non-trivial ones and visa
versa. This is because if the constructor was trivial and becomes
non- trivial then instead of initializing in the constructor body you,
without warning, begin assigning in the constructor body. The other
direction isn't bad, it just causes you to violate the policy without
being aware of it (which is why I think this policy is essentially
impossible to enforce and thus is just bad).

On the other hand, if you initialize EVERYTHING in the initializer
list you know that the members of your object are being initialized
and being initialized correctly. You don't have to consider whether
they have trivial or non-trivial constructors, whether you could
initialize in the body instead, etc... It's obvious, it's clear, and
the policy can be maintained much more easily.

Another reason to prefer a policy of initializing in the list rather
than body is that it's singular. Though people from C or whatever
might not be used to the syntax, they are simply going to have to get
so or they can not write C++ code. Some things HAVE to be initialized
in the initializer. Although you cannot construct a POD with values
in the initializer (not in C++03 and earlier anyway), you can zero
initialize anything there. You can then assign to that initialized
object in the body. So you have one place you look at and deal with
when initializing, not many. In C++ the only way to get singularity
on this issue is to use the initialization list...it's just a fact
that anyone has to deal with.

Although I can see why someone might *prefer* to see "var = xxxx"
because it's what they're used to or whatever (initializer syntax
isn't exactly pretty it must be admitted), I don't think that should
be the only consideration. IMHO a coding policy must be adoptable,
enforceable, maintainable and practical. It seems to me that this one
fails on all accounts.

Gene Bushuyev

unread,
Apr 25, 2012, 12:25:35 AM4/25/12
to
On Apr 23, 9:22 pm, Miles Bader <mi...@gnu.org> wrote:
> [I think a coding standard that _mandates_ avoiding initializers is
> pretty dopey , but:]
>
> brang...@cix.compulink.co.uk (Dave Harris) writes:
> >> > and full of restrictions and special cases;
>
> >> A list, please!
>
> > I've already mentioned them, but if you want it in list form:
> > o Order of evaluation may not be order written
>
> (but this is an easy and safe [no false positives] compiler warning)
>
> > o Unable to add more code between initialisations (so it doesn't
> > scale).
>
> This is absolutely my main complaint about initializer lists -- I
> find it a very common case that I want to calculate an intermediate
> value based on the constructor arguments, and then use that value to
> initialize several data members. The initializer-list form makes
> this quite annoying.

Not being able to draw a good example of such "a very common case"
from memory, I would suggest it's 1) not common, and 2) it's likely a
design defect rather than language problem. What you're describing is
this: your class needs to create another complex or costly object,
which it then uses as an organ donor to initialize (or assign) its own
members. Perhaps, if the class contained that complex or costly object
instead of its organs would be a better design and made this procedure
unnecessary.


--

Miles Bader

unread,
Apr 25, 2012, 2:31:11 PM4/25/12
to
Gene Bushuyev <public...@gbresearch.com> writes:
> What you're describing is this: your class needs to create another
> complex or costly object, which it then uses as an organ donor to
> initialize (or assign) its own members. Perhaps, if the class
> contained that complex or costly object instead of its organs would
> be a better design and made this procedure unnecessary.

Certainly one way of working around this issue is to add new field(s)
which store the intermediate valu(e). But if the code in the class
all operates on the derived values, and the intermediate value isn't a
"natural" part of the class data, then having this value stored in the
class is artificial -- i.e., it's a workaround, not "better design."

If the intermediate value is small / doesn't consume too many
resources, then whatever, maybe it's an _acceptable_ workaround, but
it's still a workaround.

[E.g., consider a class which takes a string value as a constructor
argument, makes a filename from it, reads some parameter values from a
file with that name, and stores the parameter values as data members
in the new object. All subsequent operations just use the parameters.]

-Miles

--
The automobile has not merely taken over the street, it has dissolved the
living tissue of the city. Its appetite for space is absolutely insatiable;
moving and parked, it devours urban land, leaving the buildings as mere
islands of habitable space in a sea of dangerous and ugly traffic.
[James Marston Fitch, New York Times, 1 May 1960]

Seungbeom Kim

unread,
Apr 25, 2012, 3:10:15 PM4/25/12
to
On 2012-04-23 21:22, Miles Bader wrote:
> bran...@cix.compulink.co.uk (Dave Harris) writes:
>> o Unable to add more code between initialisations (so it doesn't
>> scale).
>
> This is absolutely my main complaint about initializer lists -- I find
> it a very common case that I want to calculate an intermediate value
> based on the constructor arguments, and then use that value to
> initialize several data members. The initializer-list form makes this
> quite annoying.

For example,

A::A(double x, double y)
{
double r = hypot(x, y);
this->x = x / r;
this->y = y / r;
}

In this example, 'hypot(x, y)' represents an expensive function call
whose return value is to be used in initializing subsequent members.

Is there a good way to do the same with an initialization list?

--
Seungbeom Kim

James K. Lowden

unread,
Apr 25, 2012, 3:11:21 PM4/25/12
to
On Tue, 24 Apr 2012 16:15:56 -0700 (PDT)
nroberts <robert...@gmail.com> wrote:

> Although you cannot construct a POD with values
> in the initializer (not in C++03 and earlier anyway)

I'm confused by that statement. You seem to be saying the code below
wouldn't compile, but it does and I'm sure you would expect it to.

class A
{
int a;
public:
A( int a ) : a(a) {}
};

int i(17);

What am I missing?

--jkl

nroberts

unread,
Apr 25, 2012, 8:01:12 PM4/25/12
to
On Apr 25, 12:11 pm, "James K. Lowden" <jklow...@speakeasy.net> wrote:
> On Tue, 24 Apr 2012 16:15:56 -0700 (PDT)
>
> nroberts <roberts.n...@gmail.com> wrote:
> > Although you cannot construct a POD with values
> > in the initializer (not in C++03 and earlier anyway)
>
> I'm confused by that statement. You seem to be saying the code below
> wouldn't compile, but it does and I'm sure you would expect it to.
>
> class A
> {
> int a;
> public:
> A( int a ) : a(a) {}
>
> };
>
> int i(17);
>
> What am I missing?

struct X { int val1; int val2; };

struct A
{
// invalid attempt...
//A(int v1, int v2) : x(v1,v2) {}

// invalid attempt... (works in C++11 I think)
//A(int v1, int v2) : x({v1, v2}) {}

// Can only...
A(int v1, int v2) : x() // 0 init...
{
x.val1 = v1;
x.val2 = v2;
}

// Or...
A()
{
x = { 2, 3 };
}

// or...
static X initX(int v) { X x = {v, 42 }; return x; }
A(int v) : x(initX(v)) {}

private:
X x;
};

That's what I mean. A pod structure cannot be initialized to anything
but 0 in the initializer list without a helper.

Daniel Krügler

unread,
Apr 25, 2012, 8:01:39 PM4/25/12
to
Am 25.04.2012 21:11, schrieb James K. Lowden:
> On Tue, 24 Apr 2012 16:15:56 -0700 (PDT)
> nroberts<robert...@gmail.com> wrote:
>
>> Although you cannot construct a POD with values
>> in the initializer (not in C++03 and earlier anyway)
>
> I'm confused by that statement. You seem to be saying the code below
> wouldn't compile, but it does and I'm sure you would expect it to.
>
> class A
> {
> int a;
> public:
> A( int a ) : a(a) {}
> };
>
> int i(17);
>
> What am I missing?

I think that nroberts actually wanted to refer to POD classes, not to
POD types (like int) in general.

If that is not what you where trying to say: A is not a POD type in
C++03 (This would require it to be an aggregate type, thus it excludes
all types with a user-provided constructor). It is also no POD type in
C++11 but now only because it does not have a default constructor (This
is somehow an astonishing result given the fact that a deleted default
constructor is considered as trivial).

HTH & Greetings from Bremen,

Daniel Krügler

Gene Bushuyev

unread,
Apr 25, 2012, 8:07:32 PM4/25/12
to
On Apr 25, 11:31 am, Miles Bader <mi...@gnu.org> wrote:
> Gene Bushuyev <publicfil...@gbresearch.com> writes:
> > What you're describing is this: your class needs to create another
> > complex or costly object, which it then uses as an organ donor to
> > initialize (or assign) its own members. Perhaps, if the class
> > contained that complex or costly object instead of its organs would
> > be a better design and made this procedure unnecessary.
>
> Certainly one way of working around this issue is to add new field(s)
> which store the intermediate valu(e). But if the code in the class
> all operates on the derived values, and the intermediate value isn't a
> "natural" part of the class data, then having this value stored in the
> class is artificial -- i.e., it's a workaround, not "better design."
>

No, I didn't suggest any artificial intermediate values to be stored
in the class. I suggested that the composition of the class perhaps
requires the different kind of object that doesn't require carving it
to organs.

> If the intermediate value is small / doesn't consume too many
> resources, then whatever, maybe it's an _acceptable_ workaround, but
> it's still a workaround.

I disagree with suggestion of having (no matter how small) scratch
space in a class. I've seen people creating such mutable scratch
spaces, modified independently of object functionality in different
places. It creates a very messy design.

>
> [E.g., consider a class which takes a string value as a constructor
> argument, makes a filename from it, reads some parameter values from a
> file with that name, and stores the parameter values as data members
> in the new object. All subsequent operations just use the parameters.]
>

Perhaps the object constructor is not designed correctly or a
constructor is missing in that case, as there should be a constructor
taking a stream.


--

Gene Bushuyev

unread,
Apr 25, 2012, 8:08:34 PM4/25/12
to
On Apr 25, 12:10 pm, Seungbeom Kim <musip...@bawi.org> wrote:
> On 2012-04-23 21:22, Miles Bader wrote:
>
> > brang...@cix.compulink.co.uk (Dave Harris) writes:
> >> o Unable to add more code between initialisations (so it doesn't
> >> scale).
>
> > This is absolutely my main complaint about initializer lists -- I find
> > it a very common case that I want to calculate an intermediate value
> > based on the constructor arguments, and then use that value to
> > initialize several data members. The initializer-list form makes this
> > quite annoying.
>
> For example,
>
> A::A(double x, double y)
> {
> double r = hypot(x, y);
> this->x = x / r;
> this->y = y / r;
>
> }
>
> In this example, 'hypot(x, y)' represents an expensive function call
> whose return value is to be used in initializing subsequent members.
>
> Is there a good way to do the same with an initialization list?

Yes, for example:

A::A(double x, double y, double r = hypot(x, y))
: x(x/r), y(y/r)
{
}

But I would also suggest that the constructor might not need to have
the burden of doing those calculations in the first place, it's more
common for a constructor to initialize its members directly from
constructor parameters:

A::A(double x, double y) : x(x), y(y) {}

and make the caller responsible for providing the expected parameters:

double r = hypot(x, y);
A(x/r, y/r);


--

Miles Bader

unread,
Apr 26, 2012, 12:22:19 AM4/26/12
to
Gene Bushuyev <public...@gbresearch.com> writes:
> Perhaps the object constructor is not designed correctly or a
> constructor is missing in that case, as there should be a
> constructor taking a stream.

Perhaps so -- but perhaps not. Obviously the latter case is what's
being discussed here.

-miles

--
The key to happiness
is having dreams. [from a fortune cookie]

Jonathan Thornburg

unread,
Apr 26, 2012, 12:25:17 AM4/26/12
to
nroberts <robert...@gmail.com> wrote:
> A pod structure cannot be initialized to anything
> but 0 in the initializer list without a helper.

I find this statement confusing.
Notably, consider the following code:

#include <iostream>

struct pod
{
int x, y;
pod(int x_in): x(x_in), y(42) { }
};

struct contains_pod
{
contains_pod(int x_in): P(x_in) { }
pod P;
};

int main()
{
pod P(69);
std::cout << "P.x = " << P.x << "\n";
std::cout << "P.y = " << P.y << "\n";

contains_pod CP(105);
std::cout << "CP.P.x = " << CP.P.x << "\n";
std::cout << "CP.P.y = " << CP.P.y << "\n";

return 0;
}

This compiles, links, and runs without any warnings under g++ 4.2.4
-W -Wall, and the output is just what I expect:

% ./foo
P.x = 69
P.y = 42
CP.P.x = 105
CP.P.y = 42
%

This shows the POD 'struct pod' being initialized by a constructor init
list to all nonzero values, both by itself and when contained in a
larger 'struct contains_pod', without using any helper functions.

So, I guess I don't see the problem.

--
-- "Jonathan Thornburg [remove -animal to reply]"
<jth...@astro.indiana-zebra.edu>
Dept of Astronomy & IUCSS, Indiana University, Bloomington, Indiana, USA
"Washing one's hands of the conflict between the powerful and the
powerless means to side with the powerful, not to be neutral."
-- quote by Freire / poster by Oxfam

Miles Bader

unread,
Apr 26, 2012, 12:26:40 AM4/26/12
to
Gene Bushuyev <public...@gbresearch.com> writes:
> But I would also suggest that the constructor might not need to have
> the burden of doing those calculations in the first place, it's more
> common for a constructor to initialize its members directly from
> constructor parameters:

Certainly it "need not", and indeed it's very common to directly
initialize members from the constructor arguments -- but it's most
certainly not universal nor always desirable.

There's nothing wrong [conceptually] with doing non-trivial
calculations in the constructor, and often doing so provides the
cleanest and most natural interface (the shortcomings of initializers,
of course, are an implementation issue).

-miles

--
Year, n. A period of three hundred and sixty-five disappointments.

Daniel Krügler

unread,
Apr 26, 2012, 5:15:16 PM4/26/12
to
On 2012-04-26 06:25, Jonathan Thornburg wrote:
> nroberts<robert...@gmail.com> wrote:
>> A pod structure cannot be initialized to anything
>> but 0 in the initializer list without a helper.
>
> I find this statement confusing.
> Notably, consider the following code:
>
> #include<iostream>
>
> struct pod
> {
> int x, y;
> pod(int x_in): x(x_in), y(42) { }
> };

Keep in mind that your type "pod" is not a POD type, because of the lack
of a (trivial) default constructor see Clause 9 p10:

"A POD struct is [..] a trivial class [..]"

and p.6:

"A trivial class is a class that has a trivial default constructor [..]"

But 'pod' does not have a default-constructor. Nonetheless C++11 has now
realized that pod is a standard_layout type.

HTH & Greetings from Bremen,

Daniel Krügler


--

Martin B.

unread,
Apr 26, 2012, 5:16:30 PM4/26/12
to
But prior to C++11 an additional ctor wouldn't have helped, since we
didn't have delegating ctors?

cheers,
Martin

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.


Martin B.

unread,
Apr 26, 2012, 5:17:54 PM4/26/12
to
On 25.04.2012 20:31, Miles Bader wrote:
> Gene Bushuyev<public...@gbresearch.com> writes:
>> What you're describing is this: your class needs to create another
>> complex or costly object, which it then uses as an organ donor to
>> initialize (or assign) its own members. Perhaps, if the class
>> contained that complex or costly object instead of its organs would
>> be a better design and made this procedure unnecessary.
>
> Certainly one way (...) it's a workaround, not "better design."
>
> If the intermediate value is small / doesn't consume too many
> resources, then whatever, maybe it's an _acceptable_ workaround, but
> it's still a workaround.
>
> [E.g., (...)]

Miles - maybe you could dig up one concrete example from your code base?
Would help to show what you mean ...

cheers,
Martin

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.


Martin B.

unread,
Apr 26, 2012, 5:19:38 PM4/26/12
to
On 26.04.2012 02:08, Gene Bushuyev wrote:
> On Apr 25, 12:10 pm, Seungbeom Kim<musip...@bawi.org> wrote:
>> On 2012-04-23 21:22, Miles Bader wrote:
>>
>>> brang...@cix.compulink.co.uk (Dave Harris) writes:
>>>> o Unable to add more code between initialisations (so it doesn't
>>>> scale).
>>
>>> This is absolutely my main complaint about initializer lists -- I find
>>> it a very common case that I want to calculate an intermediate value
>>> based on the constructor arguments, (...)
>>
>> A::A(double x, double y)
>> {
>> double r = hypot(x, y);
>> this->x = x / r;
>> this->y = y / r;
>>
>> }
>>
>> (...)
>> Is there a good way to do the same with an initialization list?
>
> Yes, for example:
>
> A::A(double x, double y, double r = hypot(x, y))
> : x(x/r), y(y/r)
> {
> }
>
> But I would also suggest that the constructor might not need to have
> the burden of doing those calculations in the first place, it's more
> common for a constructor to initialize its members directly from
> constructor parameters:
>
> A::A(double x, double y) : x(x), y(y) {}
>
> and make the caller responsible for providing the expected parameters:
>
> double r = hypot(x, y);
> A(x/r, y/r);
>

Both your (counter-)examples are *not* convincing. They significantly
change what the constructor does. (In that a user could provide another
r / in that the hypot() function is leaked into the interface.)

I do not mind doing things in the ctor body when I must, but for me the
question remains:

Is there a good way to do the same with an initialization list?

cheers,
Martin

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.


GM

unread,
Apr 26, 2012, 5:20:54 PM4/26/12
to
Il 26/04/2012 06:25, Jonathan Thornburg ha scritto:

...
> struct pod
> {
> int x, y;
> pod(int x_in): x(x_in), y(42) { }
> };
...
> This shows the POD 'struct pod'
...

The problem is that your 'struct pod' is not a POD.
Try adding this line to your program:

std::cout << "is_pod<pod> " << std::is_pod<pod>::value << "\n";


--

Seungbeom Kim

unread,
Apr 26, 2012, 5:23:11 PM4/26/12
to
On 2012-04-25 17:08, Gene Bushuyev wrote:
> On Apr 25, 12:10 pm, Seungbeom Kim <musip...@bawi.org> wrote:
>>
>> For example,
>>
>> A::A(double x, double y)
>> {
>> double r = hypot(x, y);
>> this->x = x / r;
>> this->y = y / r;
>>
>> }
>>
>> In this example, 'hypot(x, y)' represents an expensive function call
>> whose return value is to be used in initializing subsequent members.
>>
>> Is there a good way to do the same with an initialization list?
>
> Yes, for example:
>
> A::A(double x, double y, double r = hypot(x, y))
> : x(x/r), y(y/r)
> {
> }

This change affects the function signature and the class interface.
In particular, in order to let the clients construct an A object with
only two arguments, the default argument declaration should be in the
definition of class A, i.e. usually in the header, which exposes the
implementation detail and necessitates including additional headers
(<cmath> in this example) from A.h, not from A.cpp.

> But I would also suggest that the constructor might not need to have
> the burden of doing those calculations in the first place, it's more
> common for a constructor to initialize its members directly from
> constructor parameters:
>
> A::A(double x, double y) : x(x), y(y) {}
>
> and make the caller responsible for providing the expected parameters:
>
> double r = hypot(x, y);
> A(x/r, y/r);

That's up to the designer of the class to decide, not some details of
the language syntax. The calculation may be a part of enforcing the
class invariant, which is obviously the job of the constructor(s).


I think this is a flaw in the language, though its effect may not be
that serious and you can often live with it. Whether you have an empty
body or a non-empty body for the constructor, it has a stack frame
on which any temporary objects can live, and nothing prevents you
from constructing some temporary objects there between constructing
members, except that the language doesn't have a syntax to allow it.
For example, the language could have allowed something like this:

struct A
{
double x, y;
A(double x, double y) :
double r(hypot(x, y)), // a temporary object
x(x / r),
y(y / r)
{ }
};

( This hypothetical syntax is a bit confusing here as the use of ','
between the objects makes them look like an init-declarator-list
corresponding to a single decl-specifier-seq. Things would have been
better if we used ';' instead:

struct A
{
double x, y;
A(double x, double y) :
double r(hypot(x, y));
x(x / r);
y(y / r);
{ }
};

This would have also solved the aesthetic/editorial problem of having
to omit the comma after the last member or maintaining the commas as
you add or delete members. -- I digress too much here. )

--
Seungbeom Kim

Christopher Creutzig

unread,
Apr 26, 2012, 5:27:21 PM4/26/12
to
On 4/26/12 6:25 AM, Jonathan Thornburg wrote:

> struct pod
> {
> int x, y;
> pod(int x_in): x(x_in), y(42) { }
> };

That is not a POD class, by virtue of having a constructor.


Christopher


--

nroberts

unread,
Apr 26, 2012, 5:28:44 PM4/26/12
to
On Apr 25, 12:10 pm, Seungbeom Kim <musip...@bawi.org> wrote:
> On 2012-04-23 21:22, Miles Bader wrote:
>
> > brang...@cix.compulink.co.uk (Dave Harris) writes:
> >> o Unable to add more code between initialisations (so it doesn't
> >> scale).
>
> > This is absolutely my main complaint about initializer lists -- I find
> > it a very common case that I want to calculate an intermediate value
> > based on the constructor arguments, and then use that value to
> > initialize several data members. The initializer-list form makes this
> > quite annoying.
>
> For example,
>
> A::A(double x, double y)
> {
> double r = hypot(x, y);
> this->x = x / r;
> this->y = y / r;
>
> }
>
> In this example, 'hypot(x, y)' represents an expensive function call
> whose return value is to be used in initializing subsequent members.
>
> Is there a good way to do the same with an initialization list?

This is a special case of course, and so I don't really think it makes
a valid argument to assign (not initialize, that is NOT happening) in
the constructor body. But just like I'd zero initialize my variables
if I were writing a regular function that required that a variable
exist before it can be assigned its true value (occasionally does
happen even when you can create variables anywhere), I think you
should do the same here:

A::A(double x, double y)
: x(), y() // of course you have to change names here...I think
(even if not, you should)
{
double r = hypot(x, y);
this->x = x / r;
this->y = y / r;
}

It may seem a little pedantic to do this, but I still think it's a
very good habit to get into. I *never* allow a variable to be
initialized to an indeterminate state even if I'm assigning to it
fairly immediately. Somewhere down the road someone will stick a line
between the statements or something and sometimes this can change
everything.

Of course, that's just the simple response. If hypot was really an
expensive operation that was regularly using up a lot of time in
profiles, as your optimization and assertion that it's expensive might
indicate, you could consider making it smarter. Write it so that if
you pass it the same arguments it got last time it just returns the
answer it calculated last time. A much more complicated response for
sure, but generally speaking if an operation is taking up a lot of
time in one place...it very well might be in another as well.


--

Miles Bader

unread,
Apr 27, 2012, 2:26:57 PM4/27/12
to
"Martin B." <0xCDC...@gmx.at> writes:
>> If the intermediate value is small / doesn't consume too many
>> resources, then whatever, maybe it's an _acceptable_ workaround, but
>> it's still a workaround.
>
> Miles - maybe you could dig up one concrete example from your code base?
> Would help to show what you mean ...

I think Seungbeom Kim's earlier example demonstrates exactly what I
mean.

-miles

--
values of β will give rise to dom!

Dave Abrahams

unread,
Apr 27, 2012, 2:29:10 PM4/27/12
to
on Thu Apr 26 2012, "Martin B." <0xCDCDCDCD-AT-gmx.at> wrote:

>> Yes, for example:
>>
>> A::A(double x, double y, double r = hypot(x, y))
>> : x(x/r), y(y/r)
>> {
>> }
>>
>> But I would also suggest that the constructor might not need to have
>> the burden of doing those calculations in the first place, it's more
>> common for a constructor to initialize its members directly from
>> constructor parameters:
>>
>> A::A(double x, double y) : x(x), y(y) {}
>>
>> and make the caller responsible for providing the expected parameters:
>>
>> double r = hypot(x, y);
>> A(x/r, y/r);
>>
>
> Both your (counter-)examples are *not* convincing. They significantly
> change what the constructor does. (In that a user could provide another
> r / in that the hypot() function is leaked into the interface.)

I agree that this is "jumping through hoops", but there is a standard
way to deal with that problem: use delegating constructors:

struct A
{
A(double x, double y)
: A(x,y,hypot(x, y)) {}

private:
A(double x, double y, double r)
:x(x/r), y(y/r) {}

double x, y;
};

in C++03, you do the same thing by adding a base class just for
construction purposes:

struct ABase
{
A(double x, double y, double r)
:x(x/r), y(y/r) {}

double x, y;
};

struct A : private ABase
{
A(double x, double y)
: ABase(x,y,hypot(x, y)) {}
};

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Dave Harris

unread,
Apr 30, 2012, 8:28:51 AM4/30/12
to
{ Reformatted. Please limit your lines to 70 characters -mod }

da...@boostpro.com (Dave Abrahams) wrote (abridged):
> > Both your (counter-)examples are *not* convincing. They
> > significantly change what the constructor does. (In that a
> > user could provide another r / in that the hypot() function
> > is leaked into the interface.)
>
> I agree that this is "jumping through hoops", but there is a
> standard way to deal with that problem: use delegating constructors:

True. I expect the coding policy predates C++11. C++11 adds some
features that makes initialiser lists easier to use.


> in C++03, you do the same thing by adding a base class just for
> construction purposes:

True. I'm not convinced the benefits of initialiser lists are worth
the price of adding another class and another level to the inheritance
hierarchy. Both of these techniques feel to me like they are
contorting code to solve something which isn't really a problem. They
seem more purist than pragmatic.

It reminds me of the debate over whether functions can have multiple
exit points. They can always be avoided, but for me the cost of doing
so isn't always worth it. These are areas where different programmers
can have different aesthetic tastes. It's not decided by objective
right and wrong, but by how we value different kinds of complexity.

-- Dave Harris, Nottingham, UK.


--
0 new messages