member initialization list priority

  • Thread starter Dwight Army of Champions
  • Start date
D

Dwight Army of Champions

can i use already-initialized data members further on when
initializing other members of the member initialization list?
 
Ö

Öö Tiib

can i use already-initialized data members further on when
initializing other members of the member initialization list?

No. Order in member initialization list does not affect actual order
of initialization. Actual order is first virtual bases, then bases,
then data members in same order like in class declaration.
 
F

Francesco S. Carta

on said:
can i use already-initialized data members further on when
initializing other members of the member initialization list?

The order of execution of the initializers follows the order of
declaration of the various members, as others pointed out.

If the initialization list follows a different order, then the compiler
could issue some warnings, and surely the people reading your code could
get confused.

In any case yes, you can use the already initialized values in the other
initializations:


#include <iostream>

using namespace std;

class Init {
public:
Init() : // processing order:
c(b+1), // last
b(a+1), // second
a(1) // first
{} // bad style as it could confuse the reader
void print() const {
cout << "a == " << a << endl;
cout << "b == " << b << endl;
cout << "c == " << c << endl;
}
private:
// in the initialization list the order will be
int a; // first initialization
int b; // second initialization
int c; // third initialization
// regardless of the initialization order
};

int main() {
Init i;
i.print();
}
 
Ö

Öö Tiib

Yes but .. "Order in member initialization list does not affect actual order
of initialization. Actual order is first virtual bases, then bases,
then data members in same order like in class declaration."

:)

Yes, that is also not wrong. ;) Some policies demand both orders to be
same, despite compilers do not care. It removes the yes/no situation
about the whole topic.
 
J

Jorgen Grahn

No. Order in member initialization list does not affect actual order
of initialization. Actual order is first virtual bases, then bases,
then data members in same order like in class declaration.

But your answer "no" does not follow from your explanation.

I cannot say "yes" for sure, but I often do what the original poster
asks for. My compiler (g++) warns me if my initializations aren't in
declaration order, so I feel I can safely do it.

/Jorgen
 
A

Andrey Tarasevich

Dwight said:
can i use already-initialized data members further on when
initializing other members of the member initialization list?

Yes, but keep in mind that order of initialization is the same as order
of member declaration in the class definition.

The members that are "already initialized" by the moment you get to
member M are the members that are declared before M in the class
definition.

The order in which you mention the members in the constructor
initializer list does not matter.
 
Ö

Öö Tiib

But your answer "no" does not follow from your explanation.

Uh? Possibly i misunderstood the topic and the question, then. Order
in that list does not affect initialization order and so "no" he can
not initialize other members further on the list unless that list is
made specially to follow initialization order.
I cannot say "yes" for sure, but I often do what the original poster
asks for. My compiler (g++) warns me if my initializations aren't in
declaration order, so I feel I can safely do it.

g++ is being helpful there. It helps against bad style. Program, where
member initializations are in some other order is not ill-formed.
Standard as i remember was extremely explicit about defending such
style. I have seen (and cursed) plenty of such code.
 
K

Kai-Uwe Bux

Öö Tiib said:
Uh? Possibly i misunderstood the topic and the question, then. Order
in that list does not affect initialization order and so "no" he can
not initialize other members further on the list unless that list is
made specially to follow initialization order.

The way, I read the question is whether one can do:

class range {

int low;
int high;

public:

range ( int from, int size )
: low ( from )
, high ( low + size ) // using already initialized member "low"
{}

};

[...]


Best

Kai-Uwe Bux
 
Ö

Öö Tiib

The way, I read the question is whether one can do:

  class range {

    int low;
    int high;

  public:

    range ( int from, int size )
      : low ( from )
      , high ( low + size ) // using already initialized member "low"
    {}

  };

Yes, appears i misunderstood it, perhaps because of that "further on".
As I understand the standard, such constructor must also work when low
is initialized first.

range( int from, int size )
: high( low + size ) // using already initialized member "low"
, low( from )
{}
 
V

Vladimir Jovic

Öö Tiib said:
Yes, appears i misunderstood it, perhaps because of that "further on".
As I understand the standard, such constructor must also work when low
is initialized first.

range( int from, int size )
: high( low + size ) // using already initialized member "low"
, low( from )
{}

The low is uninitialized, when the high is being initialized. Or I
misunderstood your reply.

#include <iostream>
class range {
public:
int high;
int low;

range ( int from, int size )
: high ( low + size ), // using already initialized member "low"
low ( from )
{}
};
int main()
{
range p(5,3);
std::cout<<"low="<<p.low<<" high="<<p.high<<std::endl;
}
 
S

Stuart Golodetz

Vladimir said:
The low is uninitialized, when the high is being initialized. Or I
misunderstood your reply.

#include <iostream>
class range {
public:
int high;
int low;

range ( int from, int size )
: high ( low + size ), // using already initialized member "low"
low ( from )
{}
};
int main()
{
range p(5,3);
std::cout<<"low="<<p.low<<" high="<<p.high<<std::endl;
}

His example works if you keep the declaration order within the class the
same (which seems to have been the intention) -- the two key points seem
to be:

i) Initialization order depends on the order in which you list the
members in the class, *not* on the order in the initializer list.

ii) It's valid to initialize members using other members that precede
them in the initialization order (but note that the initialization order
may not be what you initially expect, i.e. see (i)).

Cheers,
Stu
 
S

Stuart Golodetz

Stuart said:
His example works if you keep the declaration order within the class the
same (which seems to have been the intention) -- the two key points seem
to be:

Just to clarify, when I said "the same", I meant "the same as in the
post to which he was replying", i.e. low before high. Your version above
is the problematic one, because low hasn't been initialized at the point
where you try and use it to initialize high.

Kind regards,
Stu
 
V

Vladimir Jovic

Stuart said:
Just to clarify, when I said "the same", I meant "the same as in the
post to which he was replying", i.e. low before high. Your version above
is the problematic one, because low hasn't been initialized at the point
where you try and use it to initialize high.

Thank you for clarification, but next example :

#include <iostream>
class range {
public:
int low;
int high;

range ( int from, int size )
: high ( low + size ), // using already initialized member "low"
low ( from )
{}
};
int main()
{
range p(5,3);
std::cout<<"low="<<p.low<<" high="<<p.high<<std::endl;
}

fails the compilation with next errors :

g++ g.cpp -O3 -Wall -Wextra -Werror
cc1plus: warnings being treated as errors
g.cpp: In constructor ‘range::range(int, int)’:
g.cpp:6: error: ‘range::high’ will be initialized after
g.cpp:5: error: ‘int range::low’
g.cpp:8: error: when initialized here

(notice the extra compiler parameters)

1) Reading OOTib's response (and your), it looks like it should compile
2) Reading responses from others, it should not compile

What am I missing?
 
S

Stuart Golodetz

Vladimir said:
Thank you for clarification, but next example :

#include <iostream>
class range {
public:
int low;
int high;

range ( int from, int size )
: high ( low + size ), // using already initialized member "low"
low ( from )
{}
};
int main()
{
range p(5,3);
std::cout<<"low="<<p.low<<" high="<<p.high<<std::endl;
}

fails the compilation with next errors :

g++ g.cpp -O3 -Wall -Wextra -Werror
cc1plus: warnings being treated as errors
g.cpp: In constructor ‘range::range(int, int)’:
g.cpp:6: error: ‘range::high’ will be initialized after
g.cpp:5: error: ‘int range::low’
g.cpp:8: error: when initialized here

(notice the extra compiler parameters)

1) Reading OOTib's response (and your), it looks like it should compile
2) Reading responses from others, it should not compile

What am I missing?

I guess what you're missing is that things might not compile (despite
being well-defined) if you set your compiler settings in particular
ways. In particular, g++ is a compiler that warns you if your member
declaration order and the order in your initializer list don't match. If
you enable "treat warnings as errors", the warnings it issues will turn
into errors, and your code won't compile. That doesn't mean it's not
well-defined according to the standard, it just means that g++ won't
compile it with the settings you've chosen.

There are good arguments for fixing all (sensible) warnings issued by
your compiler (a few, e.g. a small number of VC++'s more idiotic
warnings, such as "X inherits Y::f via dominance", should usually be
disabled to reduce noise that hides genuine warnings). There are also
some arguments for treating warnings as errors (but I'm personally not
convinced that they're good arguments unless you're working with people
who are ill-disciplined). In this case, I would tend to keep g++ happy
by reordering my initializer list (or the order of member declarations,
in rare cases). But the code as it stands does work, however confusing
it might prove to a casual reader.

[
Aside:

I wouldn't like to swear to this (perhaps someone can clarify), but I
*think* it's the case that the standard doesn't actually say "X should
compile, Y shouldn't"; rather, it mandates cases in which compilers are
required to issue a diagnostic (that doesn't necessarily mean they have
to fail to compile your code, as I understand it). In this case,
presumably mismatched declaration and initializer list orders isn't one
of those cases, but g++ chooses to warn you by default anyway.
]

Incidentally, it also compiles for me when the order is wrong:

struct X
{
int b;
int a;

X(int a_)
: a(a_), b(a)
{}
};

int main()
{
X x(23);
return 0;
}

So I guess the issue is not primarily one of whether the code compiles
or not, but of correctness.

Cheers,
Stu
 
S

Stefan van Kessel

Thank you for clarification, but next example :

#include <iostream>
class range {
public:
int low;
int high;

range ( int from, int size )
: high ( low + size ), // using already initialized member "low"
low ( from )
{}
};
int main()
{
range p(5,3);
std::cout<<"low="<<p.low<<" high="<<p.high<<std::endl;
}

fails the compilation with next errors :

g++ g.cpp -O3 -Wall -Wextra -Werror
cc1plus: warnings being treated as errors
g.cpp: In constructor ‘range::range(int, int)’:
g.cpp:6: error: ‘range::high’ will be initialized after
g.cpp:5: error: ‘int range::low’
g.cpp:8: error: when initialized here

(notice the extra compiler parameters)

1) Reading OOTib's response (and your), it looks like it should compile
2) Reading responses from others, it should not compile

What am I missing?

gcc is just warning because the order of initialization that's actually
going to happen (first range::low then range::high) differs from the
order in which they are listed in the initializer list. The warning is
reasonable because it might lead the inexperienced or inattentive reader
to conclude that high gets initialized first and then low (which is
wrong). I don't know whose response you understood to mean that it
should not compile. But it clearly should compile (with less stringent
compiler parameters anyway) and first set low=from; and then high=low+size;

"There is a sequence point (1.9) after the initialization of each base
and member." (12.6.2/3)
"nonstatic data members shall be initialized in the order they were
declared in the class definition (again regardless of the order of the
mem-initializers)." (12.6.2/5)

Have a nice day,
Stefan
 
J

James Kanze

[...]
I wouldn't like to swear to this (perhaps someone can
clarify), but I *think* it's the case that the standard
doesn't actually say "X should compile, Y shouldn't"; rather,
it mandates cases in which compilers are required to issue
a diagnostic (that doesn't necessarily mean they have to fail
to compile your code, as I understand it). In this case,
presumably mismatched declaration and initializer list orders
isn't one of those cases, but g++ chooses to warn you by
default anyway.

You're basically right. According to the standard, if a program
is ill-formed, the implementation is required to issue
a diagnostic message (unless it is explicitely noted that "no
diagnostic is required" or the standard says that violations
result in "undefined behavior"). The standard doesn't say what
happens after that, and since "Undefined behavior may also be
expected when this International Standard omits the description
of any explicit definition of behavior", it's formally undefined
behavior---the compiler can do anything it wishes. (This
language dates from the C standard, and back when it was
adopted, one person suggested that a compiler could document
that it's diagnostic message was to turn the light on on the
hard disk for an unspecified length of time, and then format the
hard disk:).)

The logic behind this is to allow an implementation to "extend"
the language, making code that would otherwise contain
diagnosable errors part of the extension. All that was requires
was that it output a diagnostic (say along the lines of "This
code uses an extension"); it could then continue, doing whatever
it liked with the code. (In practice, all of the compilers I've
seen have preferred to use compiler options, so you can compile
in strictly conformant mode, and the the compiler is standard
compliant, or with extensions activated, and the compiler is not
compliant. IMHO, this is a better solution.)

Note too that there's nothing to forbid a compiler from
outputting a diagnostic for a correct program. A lot of
compilers do sort of distinguish, using the distinction error
and warning. But the distinction is rarely perfect---a lot of
compilers will only output warnings for certain types of
diagnosable errors.

One additional comment: the standard defines diagnostic message
as "a message belonging to an implementation-defined subset of
the implementation’s output messages", and
implementation-defined behavior as "behavior, for a well-formed
program construct and correct data, that depends on the
implementation and that each implementation shall document."
Note well the last five words: anything that is
implementation-defined must be documented. I would interpret
this to mean that the compiler must document which "warnings"
are due to "diagnosable errors", in the sense of the standard,
and which concern legal code. I've yet to find this
documentation for any compiler I've used, however. (G++, for
example, simply says that any messages that the compiler outputs
should be considered a diagnostic message in the sense of the
standard. Which is technically correct, since the standard
allows a diagnostic message even if there is no error, but it
certainly ignores the intent of the rules.)
 
S

Stuart Golodetz

James said:
[...]
I wouldn't like to swear to this (perhaps someone can
clarify), but I *think* it's the case that the standard
doesn't actually say "X should compile, Y shouldn't"; rather,
it mandates cases in which compilers are required to issue
a diagnostic (that doesn't necessarily mean they have to fail
to compile your code, as I understand it). In this case,
presumably mismatched declaration and initializer list orders
isn't one of those cases, but g++ chooses to warn you by
default anyway.

You're basically right. According to the standard, if a program
is ill-formed, the implementation is required to issue
a diagnostic message (unless it is explicitely noted that "no
diagnostic is required" or the standard says that violations
result in "undefined behavior"). The standard doesn't say what
happens after that, and since "Undefined behavior may also be
expected when this International Standard omits the description
of any explicit definition of behavior", it's formally undefined
behavior---the compiler can do anything it wishes. (This
language dates from the C standard, and back when it was
adopted, one person suggested that a compiler could document
that it's diagnostic message was to turn the light on on the
hard disk for an unspecified length of time, and then format the
hard disk:).)

Would this be a compiler running on a DeathStation 9000 by any chance? :)
The logic behind this is to allow an implementation to "extend"
the language, making code that would otherwise contain
diagnosable errors part of the extension. All that was requires
was that it output a diagnostic (say along the lines of "This
code uses an extension"); it could then continue, doing whatever
it liked with the code. (In practice, all of the compilers I've
seen have preferred to use compiler options, so you can compile
in strictly conformant mode, and the the compiler is standard
compliant, or with extensions activated, and the compiler is not
compliant. IMHO, this is a better solution.)

The principle of being able to turn conformance on/off is a good one; in
practice, though, it can break down if library code isn't
standard-compliant (e.g. the minute you turn off language extensions in
VC++, you get whole hosts of errors from legacy Microsoft headers). I
realise it's not an easy/quick job to fix all those headers, but it does
rather take away the point of having the option. YMMV of course :)
Note too that there's nothing to forbid a compiler from
outputting a diagnostic for a correct program. A lot of
compilers do sort of distinguish, using the distinction error
and warning. But the distinction is rarely perfect---a lot of
compilers will only output warnings for certain types of
diagnosable errors.

One additional comment: the standard defines diagnostic message
as "a message belonging to an implementation-defined subset of
the implementation’s output messages", and
implementation-defined behavior as "behavior, for a well-formed
program construct and correct data, that depends on the
implementation and that each implementation shall document."
Note well the last five words: anything that is
implementation-defined must be documented. I would interpret
this to mean that the compiler must document which "warnings"
are due to "diagnosable errors", in the sense of the standard,
and which concern legal code. I've yet to find this
documentation for any compiler I've used, however. (G++, for
example, simply says that any messages that the compiler outputs
should be considered a diagnostic message in the sense of the
standard. Which is technically correct, since the standard
allows a diagnostic message even if there is no error, but it
certainly ignores the intent of the rules.)

Cheers for the explanation :)
Stu
 
J

Jorgen Grahn

Uh? Possibly i misunderstood the topic and the question, then. Order
in that list does not affect initialization order and so "no" he can
not initialize other members further on the list unless that list is
made specially to follow initialization order.

Sorry for being vague. I meant your "no" should have been "yes, but
only if it's the declaration order, which it should be anyway". But
all that got examined later in the thread anyway, so no real harm
done.

/Jorgen
 
R

red floyd

The principle of being able to turn conformance on/off is a good one; in
practice, though, it can break down if library code isn't
standard-compliant (e.g. the minute you turn off language extensions in
VC++, you get whole hosts of errors from legacy Microsoft headers). I
realise it's not an easy/quick job to fix all those headers, but it does
rather take away the point of having the option. YMMV of course :)

And of course, that's simply idiocy on Microsoft's part. It would
have been
simple to implement a predefined macro _EXTENSIONS_ENABLED or some
such, when
extensions are enabled and put the following at the top of windows.h.

#ifndef _EXTENSIONS_ENABLED
#error Use of windows.h requires extensions
#endif

This would be a much better error message than the host of cascading
errors
attempting to compile windows.h in Standard compliant mode provides.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top