olcott <NoOne@NoWhere.com>: Sep 29 07:12PM -0500 On 9/29/2020 4:53 PM, David Brown wrote: > if the statements in the body are simple assignments like the ones > above, or even some function calls, then the compiler can re-arrange > them because there is no (defined) way for the program to see a difference. Okay great. How it an initialization different than an assignment under the covers in the machine code? -- Copyright 2020 Pete Olcott |
David Brown <david.brown@hesbynett.no>: Sep 30 08:26AM +0200 On 30/09/2020 02:12, olcott wrote: >> difference. > Okay great. How it an initialization different than an assignment under > the covers in the machine code? I hope I don't need to explain to you the difference in C++ between a constructor and an assignment operator? It can certainly be the case for simple types that the generated assembly code is the same for both operations - I'd expect that in this case. |
Ralf Goertz <me@myprovider.invalid>: Sep 30 10:29AM +0200 Am Tue, 29 Sep 2020 23:53:44 +0200 > above, or even some function calls, then the compiler can re-arrange > them because there is no (defined) way for the program to see a > difference. Why is it then that in struct X { int i; float z; X(float z_, int i_) : z(z_), i(i_) {} }; int main() { X x(3.2,4); } with g++ -Wall I get: ini.cc: In constructor 'X::X(float, int)': ini.cc:3:11: warning: 'X::z' will be initialized after [-Wreorder] 3 | float z; | ^ ini.cc:2:9: warning: 'int X::i' [-Wreorder] 2 | int i; | ^ ini.cc:4:5: warning: when initialized here [-Wreorder] 4 | X(float z_, int i_) : z(z_), i(i_) {} | ^ What is the point of that warning? If the compiler can (in this case actually) initialize in any order why can't (or shouldn't) I write it in any order? Even if the initialization of one member "foo" requires the other member "bar" to be initialized first the compiler can figure out the necessary order. And foo itself can't know anything about the the position of bar in the struct so that it would matter, right? |
David Brown <david.brown@hesbynett.no>: Sep 30 12:04PM +0200 On 30/09/2020 10:29, Ralf Goertz wrote: > other member "bar" to be initialized first the compiler can figure out > the necessary order. And foo itself can't know anything about the the > position of bar in the struct so that it would matter, right? You have to understand the difference between the semantics of the language, and the code generated by the compiler. The language rules say that for member initialisation like this, the order follows the order of declaration in the class, not the order in the initialisation clause. gcc helpfully warns you if the initialisation clause does not match, because there you have code that looks different from its actual meaning. Within a constructor body, the order of assignment (not initialisation) follows the order of the statements. When generating object code for all this, a compiler can re-arrange things in all sorts of ways to get more efficient object code, as long as the result is identical in terms of the observable behaviour for the program. Since C++ code cannot access either "i" or "z" within an X object before the creation is finished, it doesn't matter which one is /actually/ initialised first in the object code. In your example, since "x" is not used, the compiler won't bother creating the object at all. If you instead have: X foo() { X x(3.2,4); return x; } the compiler (in my tests) will generate a single 64-bit load instruction - it initialises both fields at the same time. But if the fields had been more complex types, with their own non-trivial constructors, the order /could/ matter and the compiler would generate code that ordered initialisation according to the rules of the language. |
Paavo Helde <myfirstname@osa.pri.ee>: Sep 30 04:04PM +0300 30.09.2020 11:29 Ralf Goertz kirjutas: > 4 | X(float z_, int i_) : z(z_), i(i_) {} > | ^ > What is the point of that warning? I have seen this warning many times after code refactoring and it's one of the most annoying ones. The compiler is trying to be too smart, or not smart enough. It sees that you have written the mem-initializers in the wrong order, and assumes you are stupid and you think this is the order in which they are initialized. Thus it "helpfully" informs you that this is not so. If the compiler were a bit smarter, it would see these initializations do not depend on each other and therefore it does not matter at all in which order they get initialized, so there is no need for a warning. Alternatively, if the compiler trusted the user to know the language rules, there would be no need for a warning if the user writes the initializers in a random order, as the user obviously trusts the compiler for putting them into correct order automatically. > any order? Even if the initialization of one member "foo" requires the > other member "bar" to be initialized first the compiler can figure out > the necessary order. The compiler is not allowed to figure out the "correct" order, it has to follow the declaration order. I guess it's not clear for everybody why language rules are written the way they are. The reason is simple, the members need to be destructed in the opposite order of creation, regardless of by which constructor or in which TU the object was constructed, and the declaration order is the only order the destructor is guaranteed to know about. And yes, sometimes the member initialization order is important. For example, if a class contains a std::thread as a member, working with the same object, then the thread member must be the last one declared, otherwise the thread may start running before the main object is fully constructed. Good luck for having the compiler to try to figure out such things automatically! |
Ralf Goertz <me@myprovider.invalid>: Sep 30 03:15PM +0200 Am Wed, 30 Sep 2020 12:04:19 +0200 > the initialisation clause. gcc helpfully warns you if the > initialisation clause does not match, because there you have code that > looks different from its actual meaning. Okay, but in the declaration you declare and nothing else. Why does it matter in the initialzation phase that you had a different order when you declared the class. Of course, if the initialization of (in my example) z depended on i like in X(float z_, int i_) : z(z_+i), i(i_) {} then the order of initialization matters. So in this case gcc could warn about the usage of uninitialized members or so. But I still couldn't care less, whether z was *declared* before i or vice versa. > non-trivial constructors, the order /could/ matter and the compiler > would generate code that ordered initialisation according to the rules > of the language. Can you give an example for that that would not be covered by the usage of uninitialzed members? And even if you can why isn't then the order of initialization the one I should care about. What I mean is why should I or the compiler care that the declaration order is different. Is that important in any way other than that it differs and the language says it shouldn't? |
James Kuyper <jameskuyper@alumni.caltech.edu>: Sep 30 09:31AM -0400 On 9/29/20 4:51 PM, David Brown wrote: > And as always, the compiler can re-arrange that into any order it wants > if the difference can't be seen by code. (That applies to both the > member initialisation part, and the constructor body.) Correct - which means that the issue only comes up when the order in which the initializations occur does matter. If that is the case, you can guarantee that the observable results are the same "as if" the initializations had occurred in the required order, by specifying that order in the constructor, even if that's not the same as the declaration order (modulo access controls). |
Ralf Goertz <me@myprovider.invalid>: Sep 30 03:41PM +0200 Am Wed, 30 Sep 2020 16:04:39 +0300 > otherwise the thread may start running before the main object is fully > constructed. Good luck for having the compiler to try to figure out > such things automatically! But that does only mean that I need to initialize in the correct order. But the declaration order only matters because the language standards say so, right? |
Manfred <noname@add.invalid>: Sep 30 05:23PM +0200 On 9/30/2020 3:41 PM, Ralf Goertz wrote: > But that does only mean that I need to initialize in the correct order. > But the declaration order only matters because the language standards > say so, right? The initialization order is important. And the standard tells you that in order to control the initialization order you have to arrange the /declaration/ order accordingly. I would also like to stress that the initialization order is important to be univocally defined for self-consistency of the object itself, i.e. for the object to work. Paavo has already mentioned the destructor requirement of reverse destruction order (which is both a consistency requirement and a performance enabler). More generally member objects may interact with each other, so a well-defined unique order of initialization is required for this to work. Since there can be multiple constructors, the natural choice is the order of declaration, which is guaranteed to be univocal across all translation units. |
Paavo Helde <myfirstname@osa.pri.ee>: Sep 30 07:53PM +0300 30.09.2020 16:41 Ralf Goertz kirjutas: > But that does only mean that I need to initialize in the correct order. > But the declaration order only matters because the language standards > say so, right? The requirement that members are deleted in reverse order of creation is a hard one, this is related to RAII and without that guarantee it would not be possible to build robust solutions if there are dependencies between members (and if there are no such dependencies, then the order of construction is not relevant at all, so no need to worry about that). Thus, knowing the order of construction is needed for the destructor at least. Theoretically it might be possible to record the order dynamically in the constructor and store it inside the object, but this would make the object larger and the destructor code more complicated. Also, this would imply that different constructors may construct the members in different order, making their dependencies and the life of the programmer much more complex. In short, it would introduce some scripting-language-like dynamic indeterminism into the C++ fundamental data structures, for very little benefit. I believe nobody wants to go that way. If you really want to create member objects dynamically in arbitrary order, use e.g. std::map<std::string, std::any>, it's not that it is not possible to do such things in C++. |
olcott <NoOne@NoWhere.com>: Sep 30 12:24PM -0500 On 9/30/2020 1:26 AM, David Brown wrote: > constructor and an assignment operator? It can certainly be the case > for simple types that the generated assembly code is the same for both > operations - I'd expect that in this case. Someone here seemed to indicate that Initialization may require fewer assembly language steps than construction. -- Copyright 2020 Pete Olcott |
David Brown <david.brown@hesbynett.no>: Sep 30 09:58PM +0200 On 30/09/2020 19:24, olcott wrote: >> operations - I'd expect that in this case. > Someone here seemed to indicate that Initialization may require fewer > assembly language steps than construction. Initialisation always involves construction, so that statement does not make sense. For simple types with trivial construction and assignment, such as those in examples in this thread, initialisation and assignment will likely give the same generated code. |
David Brown <david.brown@hesbynett.no>: Sep 30 10:02PM +0200 On 30/09/2020 15:15, Ralf Goertz wrote: > Okay, but in the declaration you declare and nothing else. Why does it > matter in the initialzation phase that you had a different order when > you declared the class. Why does initialisation order follow declaration order? Because that's how the language is defined. I don't know /why/ it is defined that way, but presumably it makes sense in some way. Or do you mean, why does gcc (with the right flags) warn you when your initialisers don't follow declaration order? That's simpler - as I said, it is warning you because your code /appears/ to mean an order that is different from the /actual/ order. >> of the language. > Can you give an example for that that would not be covered by the usage > of uninitialzed members? Make some classes whose constructors print out a message. Then make a class containing members of those classes. The order of initialisation of the members of the big class are then important to the output of the program. |
James Kuyper <jameskuyper@alumni.caltech.edu>: Sep 30 05:01PM -0400 On 9/30/20 4:02 PM, David Brown wrote: > On 30/09/2020 15:15, Ralf Goertz wrote: >> Am Wed, 30 Sep 2020 12:04:19 +0200 ... > Why does initialisation order follow declaration order? Because that's > how the language is defined. I don't know /why/ it is defined that way, > but presumably it makes sense in some way. Leaving the order unspecified would cause problems in those cases where the order matters. Having initialization order follow declaration order is pretty much the simplest rule that could be defined, and puts the order fully under the control of the class definition. I doubt that there's any more complicated reasons than that for this decision. ... > class containing members of those classes. The order of initialisation > of the members of the big class are then important to the output of the > program. Ralf: keep in mind that Dave's suggestion of printing merely serves as an example. Any other shared resource would serve equally well as a reason why the order would matter. |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |