Although I suppose this is not the answer, otherwise this would be a very dull post. What's the real answer?
Jeff
proxima centaur
June 27th, 2002, 09:59 AM
I'd have the same answer as jfaust
zdf
June 27th, 2002, 10:07 AM
Originally posted by proxima centaur
I'd have the same answer as jfaust
It is not the right answer. I also have been misled by that code.
Graham
June 27th, 2002, 10:10 AM
Depends whether the line "int B::j = i + 5;" indulges in some form of Koenig lookup or similar. If not, B::j == 8, if it does, then all bets are off, since B::i has no value (or is it 0?) at the time of the statement.
kjetilh
June 27th, 2002, 10:27 AM
The result will be:
j=10
i=5
....because b::i is initialized before b::j
Philip Nicoletti
June 27th, 2002, 11:09 AM
Well, I tested the code on 2 different compilers,
GNU g++ and pgCC (Portland group).
Both gave the same answer, and the same answer that
kjetilh gave.
To be honest, I can not figure out why. Stroustrup says
that the variables will be initialized in the same order
as their definition, and makes a point of saying that it not being in
the same order as their declaration.
And when the definitions are changed to :
int B::j = i + 5;
int B::i = j + 5;
the results are consistent with Stroustrup's book.
proxima centaur
June 27th, 2002, 01:41 PM
yet, this is a bug or undefined result in VC++.
the i refered by
int B::j = i + 5;
is clearly the i from the global scope:
static int i = 3;
to obtain 8 as a result for B::j one has to replace i declaration with
Originally posted by proxima centaur
yet, this is a bug or undefined result in VC++.
the i refered by
int B::j = i + 5;
is clearly the i from the global scope:
static int i = 3;
to obtain 8 as a result for B::j one has to replace i declaration with
int B::j = ::i + 5;
Now why is that? beats me.
I use VC++ and I got the same results as Philip Nicoletti.
“An initializer for a static member is in the scope of the member’s class.” (TC++PL, 2nd edition). That is:
int B::j = i + 5; // equ int B::j = B::i + 5;
Graham
June 28th, 2002, 03:38 AM
That's what I was thinking of when I suggested "something like Koenig lookup". In defence of the rule, it's like the way a member function definition bring things into scope in the .cpp file:
class foo
{
public:
class innerclass;
//.....
void bar(innerclass&);
};
void foo::bar(innerclass& ic)
{....
In the definition, you don't have to qualify "innerclass", since it's brought into scope by "foo::bar".
With the example given, B::i has "tighter" scope than ::i, so is used in prefence. The only issue that I couldn't look up is whether B::i is initialised at that point (declaration order) or not (definition order). It just emphasises the point "define/initialise member variables in the same order that you declare them"
Anthony Mai
June 28th, 2002, 09:50 AM
This is a stupid question.
Why would any one write a piece of code like this that could easily trick the majority of programmers ( and could trick the compiler just as easy, too). As shown in previous follow ups, most people gets it wrong.
As a member of a software development team, your responsibility is NOT to write a piece of "smart" code that only you can understand correct and that is deemed to be mis-interpreted by other programmers, and even mis-compilered by the compiler, too! If you do so it doesn't show you as smart, it shows you as stupid.
Your responsibility, is to write crystal clear code which leaves absolutely no room for ambiguity what you intend to do and what happens first and what happens next. It needs to be so readable that even a junior programmer can read and understand it correctly. This is how a software team can produce bug free software.
jfaust
June 28th, 2002, 09:54 AM
Anthony,
That's not the point of the post. It's a piece of trivia, a point of interest, a starting point for discussion.
This was obviously not production code. Why attack it?
Jeff
zdf
June 28th, 2002, 10:27 AM
Anthony,
You are mean… and I suspect you love it! :D
I do not think you are so stupid not too see the point of this discussion. Relax! This is not an arena!
I am waiting for your explanations. So far I do not have any. This weekend I will give a look at Mr. Stroustrup’s book and maybe I’ll find the answer.
I am not good at English, so please be brief.
:) Thanks Jeff!
proxima centaur
June 28th, 2002, 12:54 PM
Even though it is not "good" coding practice, it is still relevant to some of us who like to look at the arcane side of things and understand the twitches of such and such obscure code.
Personally, even though I wouldn't in my right mind write a piece of code like this one, I find it interesting to understand how the compiler works. This can actually prevent me for doing stupid things in my code in the future.
Now, unless you wrongfully felt your pride diminished because you didn't get the answer right or because you feel you are far superior to the lesser of us because you know what "good" code is, please stop telling others how stupid they are, as I myself find your comment useless as it brings nothing as to why the compiler acts the way it does.
By the way, I totally agree with the rest of your comment.
Anthony Mai
June 28th, 2002, 02:04 PM
To find out why you are getting 10, 5, instead of 8,5, what you need to do is not to read a book, but see exactly what the compiled code look like.
Let me quote your code again here:
class B
{
public:
static int i;
static int j;
};
static int i = 3;
int B::j = i + 5;
int B::i = 5;
Pay attention to the last three lines. If you looked at the compiled code, you will realize that for those three lines, two of them compiles into NOTHING, only the the middle line compiles into something that is executed at run time.
Why? Because that two line has already been resolved by compiler at compiler time. So there is no need to execute anything for that two line. The compiler leaves only the middle line to be executed at run time.
That means, when the program is loaded, even before the very first line of code is executed, the global int i is already 3 and the static class member i is already 5. So when the code execute
int B::j = i + 5;
the result is 10.
Does that explaination make sense?
Anthony Mai
June 28th, 2002, 02:10 PM
BTW in
int B::j = i + 5;
The i is the class member (B::i), not the global i. When you have name conflict, the compiler will always assume the variable is the one with smallest scope. It first tries to recognize the variable as a local variable, and then as a class member, and then as a variable in current name space, and then a global variable with no name space.
PaulWendt
June 28th, 2002, 03:00 PM
Anthony Mai said:
>>
The i is the class member (B::i), not the global i. When you have name conflict, the compiler will always assume the variable is the one with smallest scope. It first tries to recognize the variable as a local variable, and then as a class member, and then as a variable in current name space, and then a global variable with no name space.
<<
I'm not doubting you at all, but this is exactly why he was posting this. When looking at
static int i = 3;
int B::j = i + 5;
int B::i = 5;
on the second line, I would think the i is the global i. It doesn't make sense to me for i to be considered B::i because we're not within the scope of a function of B. In other words, it looks to me as if these two statements should be equal:
int B::j = i + 5;
int B::j = ::i + 5;
They are not.
--Paul
Anthony Mai
June 28th, 2002, 03:16 PM
I do not see any thing wrong that in:
int B::j = i + 5;
i is assumed to be a static member of the B class. Consider this: The B:: is a scope operator that assigns the class scope to the whole line of code, until it sees a ';', or until it sees the beginning and ending {}, like the case in member functions.
Graham
June 28th, 2002, 03:21 PM
The point of the exercise is that it illustrates something that is generally not appreciated until the day it hits you and your left scratching your head wondering what's going on. This is very similar to the answer to another thread (about inheritance problems): it's to do with name hiding.
What Anthony Mai left out of his explanation is that the compiler will only go to an outer scope if it finds no matching name in the current scope.
Modify the example slightly:
#include <iostream>
using namespace std;
class B
{
public:
static SomeClass i; // SomeClass does not have operator+ defined
static int j;
};
static int i = 3;
int B::j = i + 5; // Fails to compile
int B::i = 5;
The compiler looks for something named "i" in the current scope and, because it finds one, it stops looking. Because B::i has no operator+ defined, the line fails to compile, even though there is a candidate i which would work in the enclosing scope.
PaulWendt
June 28th, 2002, 03:52 PM
Anthony Mai said
>>
Consider this: The B:: is a scope operator that assigns the class scope to the whole line of code, until it sees a ';', or until it sees the beginning and ending {}, like the case in member functions
<<
If this is how most compilers work, NOW it makes sense.
zdf
June 28th, 2002, 05:47 PM
What I have tried to point out with this exercise is:
1. Initialization order of nonlocal static objects
2. The scope of initializers.
This is what I have found about this problem:
“An initializer for a static member is in the scope of the member’s class” (TC++PL, 2nd edition). e.g.:
int B::j = i + 5; // eq int B::j = B::i + 5;
“The initialization of nonlocal static objects is done before the first use of any function or object defined in that translation unit.” (TC++PL, 2nd edition). e.g.:
int B::j = i + 5; // B::i already initialised
int B::i = 5;
I have failed to give the right answer when I first saw the problem.
A colleague of mine discovered this problem. I do not know what he has tried to do with this code. It is really strange. First of all because you expect the execution of initializing sequence to be sequential. Right now I do not see any practical use for it (any idea?). The original problem missed the global i.
zdf
June 28th, 2002, 05:57 PM
Originally posted by Anthony Mai
To find out why you are getting 10, 5, instead of 8,5, what you need to do is not to read a book, but see exactly what the compiled code look like.
The compiler may help you to find the result… the explanation may be another story.
I do not see how the compiler can help you to solve the following problem.
#include <iostream>
using namespace std;
void foo(int i, int j, int k)
{
cout << i << j << k << endl;
}
int main()
{
int l = 0;
foo( l++, l++, l++ );
return 0;
}
I am sure you already have the answer.
Anthony Mai
June 28th, 2002, 06:23 PM
Again it is plain stupid to jam several operations whose sequence matters into one statement, and leave it in ambiguity what happens first and what happens next. And frankly you don't have any thing to gain from attaching the ++ directly in place, versus putting it in a separate line of statement. The code size is not going to be smaller, nor will it be faster.
In your case you will get 000, rather than the 012 or 210 some may have expected. The standard says that by post-fxing the ++, the variable will be incremented AFTER the value is used. In this case the value is not considered used until after it is used to make the function call, and the call returns.
Actually the compiler is doing it right as long as it increment the variable any time between when the first variable is pushed to the stack and when the next statement begins.
So practically you could have various compilers returning 000, 011, 012, 001, and they are all correct results.
zdf
June 28th, 2002, 07:05 PM
Originally posted by Anthony Mai
Again it is plain stupid to...
Please, do not forget: it is just an exercise.
Could you please comment on the following variation?
foo( ++l, ++l, ++l );
I’m going to bed. See you!
Graham
June 29th, 2002, 05:21 AM
Comment on variation (and original):
Do not use increment or decrement operators more than once on the same entity between sequence points
zdf
June 30th, 2002, 07:08 AM
Hi Anthony,
The answer I was expecting from you is:
“The order of evaluation of arguments is undefined; take note compilers differ. All side effects of argument expressions take effect before the function is entered.” (TC++PL, 2nd edition)
That’s why I said I do not see how the compiler can help you.
I really enjoyed this coding example as an exercise, however it would definitely be considered "bad style" to rely on the initialization order of statics in your design.
Nonetheless, I still think that the answer is undefined. In addition there were many differing interpretations listed thusfar.
It is specified that B::i will be initialized before B::j (order of initialization for member variables follows order of class variable declarations). However this is not relevant since the initialization of B::j used, in fact, the global, static variable ::i. At the time of initializing B::j (which should be initialized to ::i + 5), the value of ::i is undefined. There is no specification regarding the order of initialization for statics/globals in relation to static member variables.
Can anyone verify this language specification? This one is a real brain twister.
Chris.
:rolleyes:
posty68
July 2nd, 2002, 10:08 AM
Can i just say that ive just read through all of these posts and what a great post !!
Lets have some more of these , by reading the problem and then following all the thoughts of people i have learn't and understood something new today ...
Cracking !!
:D
Graham
July 2nd, 2002, 11:12 AM
dude_1967: the initialisation B::j uses B::i not ::i. The use of B:: scope resolution brings class B into scope for the rest of the line, so B::i hides ::i.
dude_1967
July 2nd, 2002, 11:24 AM
Indeed Graham. Thanks.
And ::i is not used in main.
And the answer is... once and for all... 10, 5
Whew that was enough for me.
Chris.
;)
Chambers
July 3rd, 2002, 10:30 AM
So by using B::j = i + 5, what is actually interpreted by th compiler is:
( with scope B:: ) j = i + 5;
Problem solved.
:D
Bengi
July 3rd, 2002, 02:45 PM
fun..
now try this nice trick, no debugger/watcher ;)
int a=0;
int b;
b=(1||(++a))+2;
a=?
b=?
;)
dude_1967
July 3rd, 2002, 03:08 PM
OK Bengi I'll bite...
The 1 or'ed with the pre-increment of a is a logical expression (basically 1 || 1). The result of the logical expression is 1, since it is non-zero (yes, this is specified in C as well as C++).
Finally a == 1, b == 3.
Used no compiler or debugger.
Chris
:eek:
Paul McKenzie
July 3rd, 2002, 03:15 PM
a = 0
b = 3
Reasons: Short circuit evaluation.
When the expression
(1 || (++a))
is encountered, the evaluation of the || is done from left to right. Since a '1' is encountered first, this automatically makes the expression true (or 1), regardless of the value of "++a"'. Therefore the compiler skips over the (++a) due to short circuit evaluation.
And of course, 1 + 2 = 3.
Regards,
Paul McKenzie
dude_1967
July 3rd, 2002, 03:29 PM
Thanky Paul,
After filing my answer I ran to the compiler and compiled the code snippet and ran it.
I had unfortunately confused (1 || 0[no need to evaluate]) with (1 && ++0).
Thanks for clearing it up.
The exercise was another neat one.
Chris.
:eek:
zdf
July 3rd, 2002, 03:30 PM
Bengi,
Your enthusiasm is pleasant. Nonetheless I guess you had better start another thread since the subject is far from initial purpose of the post.
Please note that you may find compilers (I guess from Borland) where you can choose between complete and normal Boolean evaluation. In this case the result is a==1, b==3 in contrast with the normal result: a==0, b==3.
Keep the enthusiasm!
Bengi
July 4th, 2002, 06:41 AM
heh, actually under Borland / Visual C
they give sae result, so i guess other compilers will do the same.
since it boolean rules.
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.