Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!rutgers!bellcore!faline!thumper!ulysses!andante!alice!ark From: ark@alice.UUCP (Andrew Koenig) Newsgroups: comp.lang.c++ Subject: Re: More nits to pick at... Message-ID: <9291@alice.UUCP> Date: 3 May 89 06:16:50 GMT References: <190@riunite.ACA.MCC.COM> <9279@alice.UUCP> <193@riunite.ACA.MCC.COM> Organization: AT&T Bell Laboratories, Liberty Corner NJ Lines: 374 In article <193@riunite.ACA.MCC.COM>, rfg@riunite.ACA.MCC.COM (Ron Guilmette) writes: > I would like to extend my warm thanks to Andrew Koenig for taking the time > to formulate concise (and precise) answers to my second set of nit picking > questions. I hope that other readers of this newsgroup will benefit from > the public availablity of this information at least as much as I have. Thank you. > I > also hope that Bjarne may be able to make some use of my tendency to nit-pick. Absolutely. He reads this stuff too. > Specifically, I hope that some of the questions and answers exchanged here > will help to make the forthcomming new reference manual a more robust > source of information for other nit-pickers like me. I do too. > Although Andrew's answers were all quite clear and to the point, there are > one or two minor nits I feel compelled to re-pick. No problem. > Why not just round it off and call it Independence Day? That's pretty > close but gives you about an extra week? :-) Actually we have to have final code ready well before then so the people who duplicate tapes, print manuals, etc. can do their thing. > >How can you declare a function which can accept a pointer > >to itself as an argument (in a type-safe manner of course)? > > > > You can't in C++ or C. > > Well, so is anybody (other than me) concerned about this? Sure. But suppose C++ came up with a syntax for doing such a thing. How would you translate it into C if C can't express it? In such knotty cases, C++ tends to take the conservative course -- don't allow whatever it is until there's a strong argument for doing so. I haven't seen many applications for passing functions to themselves, perhaps in part because you can't do so in C in a type-safe way. Of course, there are various cheats with ... you can use if you really need to do it. > OK. So is there any reason (good or otherwise) for the rule that > says that that each user-defined operator must accept at least one > struct/class/union parameter? Yes. The motivation is to make it impossible to redefine the meaning of built-in operators, types, etc., as an aid toward preserving programmer sanity. > could not be relaxed so as to say that each operator must accept at > least one struct/class/union/enum? There's a very casual built-in conversion from enum to int, which implicitly defines a number of built-in operators with enum arguments: enum e { a, b, c, d }; e x = b; x++; If it were possible to redefine these operators on enum arguments, that would be changing the meaning of built-in things. C++ shies away from that. > An example: > struct base { > virtual first () {} > virtual second () { second (); } > }; > struct derived : public base { > virtual first () {} > }; > void test () > { > derived d; > d.second (); > } > In this case, which version of first() will get called? I presume that you intended base::second() to call first() and not second() which would be a recursion loop. If so, it calls derived::first(). > So the call (within the constructor) to "one of B's virtual functions" obviously > *does not* go through the vtable. Fine. To that extent, you are obviously > correct in saying that things behave "as if B were being constructed in > isolation". What happen however if we are constructing a "D_object" and > we thus (as a consequence) end up executing B's constructor, and then B's > constructor invokes one of B's virtual methods, and ***then*** the given > virtual method of B itself invokes another one of B's virtual methods (which > happens to also be overloaded in D)? While the B that makes up the D is being constructed, it's a B. It can call as many virtual functions as it likes, but it only gets virtual functions from B or its base classes, never from classes derived from B. One way to look at it is that while B's constructor is executing, the vtable is set to identify the object as a B. Only after the constructor returns is the vtable set to say it's a D. > *** This is *not* an idle question. You said that things behave "as if B were > being constructed in isolation" (i.e. without an knowledge of the fact that the > B being constructed is a part of a D). Is that true ALL THE WAY, or only as > long as you are doing operations from directly within B's constructor? All the way. > Sorry. This was a dumb question, but I feel a bit vindicated because I have > just noticed that your answer is somewhat incorrect. On pages 8-10 of the 1987 > Workshop Proceedings the ability to specify various constructors which can > themselves specify various construction orderings is described. Times change. In C++ 2.0, the order in which sub-objects are constructed is entirely controlled by the declaration order. In particular: struct A { /* stuff */ }; struct B { /* more stuff */ }; struct C { A a; B b; C(/* args */): a(1), b(0) { /* stuff */ } C(/* different args */): b(1), a(0) { /* other stuff */ } // and so on }; In C++ 2.0, constructing a C object always constructs the `a' and `b' members first, in that sequence, because `a' was declared before `b'. > page 10 (paragraph #2) when discussing order of destruction, its says: "There > is no way for the programmer to control this order". > This seems very asymmetric to me. Is there any (good) reason for this asymmetry? The reason to fix the construction and destruction order is to ensure that things are destroyed in FIFO order without having to remember, dynamically, the order in which the sub-objects of an object were constructed. There are many reasons why this is useful, most of them arcane. > >Is it legal to declare a function as inline and later give > >either another declaration or a definition for the function > >whose heading does *not* include the keyword "inline"? > OK. New question. If you can do this, then following the later "non-inline" > declaration/definition should the compiler still consider the function to be > a candidate for inlining? Yes. Once you've said `inline' the compiler is allowed to try to inline it. > > Yes, but if you want to declare a function inline, > > you must do so before the first place you use it. > Otherwise what? Are you saying that the following code contains an error? If > so what line is it on? > void function (); > void test () > { > function (); > } > inline void function () > { > } It is indeed an error. The diagnostic from cfront is `function called before defined as inline' and it points to the call: function (); I wouldn't be particularly upset at a C++ implementation that put the error message on the definition of function(). > OK. So enum type are *not* treated like int's! Ah, ha! Which of the > following things are legal, which are illegal? > enum color { red, orange, yellow }; > int array[color]; // legal ? No -- color is a type, not a value. > color c, c2; > int i; > char* cp; In these examples, note that you can freely convert an enum to int. > i = c; Yes. > c = i; No -- converting int to enum requires a cast: c = (color) i; > c = -c; // unary negation ? No, because -c is an int that requires a cast to convert it back to color. > c = c + c; > c = c - c; > c = c * c; > c = c | c; // bitwise operators ? These are all errors because you can't freely convert int to color. > i = c == c2; // simple comparisons ? > i = c > c2; // ordered comparisons ? > i = c == i; > cp = &cp[c]; // index > cp = cp+c; // pointer arithmetic These are all legal. > Don't be sorry. I'm sorry for you! :-) GNU C++ allows disambiguation > of a function reference via casting, i.e.: > void* p = (function_of_one_int) &my_overloaded_function; > This appears to be a reasonable thing to allow, and I'm glad that GNU C++ > allows it (even if cfront 2.0 may not). GNU G++ has many extensions and many other restrictions. Anyway, casts of this sort are less obvious than they appear. What about this one? void* p = (void*) &my_overloaded_function; > This relates to an earlier question I had asked. You can't do it in > a strictly type-safe way. But how about this: > class C; > typedef void (C::*Cmp) (int, ...); > class C { > public: > void member (int i = 99, Cmp ptr = &member) {} > }; No, because &member means &this->member, which is illegal. Even if it were legal, it would be the wrong type -- you need to say &C::member. Even if that would work, you can't take the address of C::member until you've defined it. The following convoluted thing appears to work, though. I don't recommend it, but here it is for the record: class C; typedef void (C::*Cmp) (int, ...); class C { public: void member (int, Cmp); }; void C::member (int i = 99, Cmp p = (Cmp)&C::member) { } Why are you trying to do this, anyway? Are you writing a threaded-code interpreter or something? > > This brings up another question which I forgot to ask. Is it legal to use a call > to a member function as part of the default value expression for a parameter of > another function within the same class, i.e.: > > class C { > public: > int member_1 () { return 99; } > void member_2 (int i = member_1 ()) {} > }; No -- this is an implicit reference to `this' > This *is* surprizing. Tell me something. I got the impression that one reason > (in cfront 1.2, g++, etc.) that you would *avoid* using an "overload" declaration > for a given global function was that you could then be sure that the name of that > function would *not* be mangled on its way to assembly (or C) code. This was/is > useful in cases where you want to link your C++ programs to code written in some > other language. Are you saying that in 2.0, *all* the names of all global > functions will be mangled and that there will be no way to prevent this? No -- in 2.0 you say which functions are C rather than saying implicitly which ones aren't. For example: extern "C" double sqrt(double); That says that sqrt(double) is a C function and should be compiled in whatever way is acceptable to your local C implementation. You can still overload it without formality: extern "C" double sqrt(double); extern complex sqrt(complex); Of course you can only say extern "C" for a single function of a given name. Particular implementations can assign whatever meaning they like to the quoted string, except that every C++ implementation is expected to allow "C++" and every one that intends to coexist with C must allow "C". One can imagine "Pascal", "Fortran", and so on. > G++ allows you to explicitly declare the return type of constructors as type > "pointer-to-class" where "class" is the class being constructed. Similarly, > it allows you to explicitly declare the type of any destructor to be "void". > Are you saing that G++ is wrong in both cases? It's either wrong or an extension, depending on your point of view. > I don't believe that that is true. Can you quote a page number for me on this? > Perhaps Bjarne will leap in at this point. Actually, it has been my understanding > that the following is legal: > > class C { > public: > void member_1 (int i, int j); > > void member_2 () { member_1 (88, 99); } > > void member_1 (int i, int j = 88); > > void member_3 () { member_1 (77); } > > void member_1 (int i = 22, int j = 33) > { > } > }; I'll ask him. I never write code like this, so I haven't had to think about it. -- --Andrew Koenig ark@europa.att.com