Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!csd4.milw.wisc.edu!dogie.macc.wisc.edu!uwvax!rutgers!att!ulysses!andante!alice!ark From: ark@alice.UUCP (Andrew Koenig) Newsgroups: comp.lang.c++ Subject: Re: More nits to pick at... Message-ID: <9279@alice.UUCP> Date: 2 May 89 04:07:44 GMT References: <190@riunite.ACA.MCC.COM> Organization: AT&T Bell Laboratories, Liberty Corner NJ Lines: 353 Here's my try at answering Ron's questions. My comments are indented: ------------------------------------------------------------------------ Listen friends... Is it just me or is this language difficult to understand and/or not rigidly specified at this time? A major factor in how easy most languages are to understand is how many of your immediate colleagues are using it. If a lot of them are, you can walk across the hall and ask for help if you get stuck. People had plenty of trouble understanding C while it was getting started. C++ is now about as far along as C was in 1979. The reference manual now in the works will go a long way toward a more solid specification. (Actually, I already know the answer. That is just my way of trying to excuse myself for pestering you all with all of my nit picking questions. I hope that I will be forgiven if I occasionally break into the middle of these Socratic discussions of the true meaning of "polymorphism" to ask some rather more mundane questions about C++ which the poor schmucks down in the trenches {like me} may need to know about.) Fair enough. The answers that follow are all based on C++ 2.0. For that matter, several of your questions seem to refer to things that are present only in C++ 2.0. Bjarne intends to make the reference manual generally available when it's ready, along with a (long) paper describing all the stuff in 2.0 that's not in the book. So here is edition #2 of my nit picking questions. I hope that even the language lawyers will find at least one brain teaser in here. As before, I would welcome *any* responses from *anybody* who feels that they may have a handle on how things really are (or better yet how they should be, or how they will be when "2.0: The Movie" arrives for your Summer (Ahem... Fall? ... Winter?) viewing pleasure.) :-) Summer. We're still on target for June 30. ----------------------------------------------------------------------- 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. You can now inherit a base class as either public or private. Can you inherit a base class as "protected"? If so, what does this mean? You can't inherit a base class as protected. We need more experience with the present protection semantics before introducing new notions. There are plenty of new notions possible -- the hard part is picking the right ones. Is it legal to declare an operator which takes no struct/class parameters but which does take at least one union parameter? At least one enum parameter? A union is a class, so yes. An enumeration isn't a class, so no. Is it an error if there is either an implicit or an explicit call to a constructor (or a destructor) for a given class at a point where the given constructor (or destructor) is not "visible" because it is either private or protected? Yes. If a constructor makes calls to virtual member functions which are declared (and defined) for the constructor's own class, do these calls ever go through the virtual function table? If so why? Suppose that class D is derived from B. Then constructing a D involves constructing a B first. Objects are constructed in exactly the same way regardless of whether they are part of other objects. Thus while the B is being constructed, it has no knowledge of whether or not it's part of a D. It follows from this that if B's constructor calls one of B's virtual functions, directly or indirectly, the behavior is as if the B were being constructed in isolation. You get the function from B or one of its base classes, but never the function from D or any other derived class of B. Is there any way to specify (explicitly) the order in which members and base classes are destructed? They are destroyed in inverse order from construction. There is no way to control the order of destruction except by ordering your declarations to control the order of construction. Why would you want to? 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"? Yes. Is it legal to declare a function as non-inline and later to redeclare it (or give a definition for it) which is inline? Yes, but if you want to declare a function inline, you must do so before the first place you use it. Is it legal for a member function to be both inline and virtual? If so, should the "inline" keyword come before or after the "virtual" keyword? Yes it is legal. However, it is never necessary to say `inline' and `virtual' together. Example 1: struct X { virtual void f() { /* ... */ } }; Here, X::f is inline by virtue of being defined (not just declared) inside a class definition. There is therefore no need to say `inline' (but it's legal, either before or after `virtual'). Example 2: struct X { virtual void f(); }; inline void X::f() { /* ... */ } Here the declaration and definition are separate, so there's still no need to say `inline' and `virtual' together. Although it's never necessary, it's generally legal; in such contexts, they may appear in either order. Are enum type values always treated like int type values or can they be used to disambiguate a function call, i.e.: enum color { red, yellow }; enum fruit { banana, apple, orange }; void overloaded (enum color); void overloaded (enum fruit); void overloaded (int i); ... overloaded (red); overloaded (apple); overloaded (99); ... Enumerated types are separate types, so they can be used to disambiguate a function call. Your example above is legal. The following, though, is not: enum color { red, yellow, orange }; enum fruit { apple, orange, banana }; What would `orange' represent? What type would it be? At the end of section 8.9 in the reference manual there is a short note about how to get the address of a particular instance of an overloaded function. It says: "The address-of-operator & may only be applied to an overloaded name in an assignment or initialization where the type expected determines which function to take the address of." Here, the term "the type expected" is not really defined. It is thus, not clear (as it is in the definition of the Ada language) what elements of the context may (or may not) be considered when a "standard conforming" compiler attempts to perform disambiguation. Specifically, I would like to know if the following should be considered legal (i.e. unambiguous): class A { ... }; class B { ... }; void overloaded (A); void overloaded (B); void* pointer = (void (*)(A)) &overloaded; Sorry. The only places you can do overloaded selection is in assignment and initialization. However, this is OK: void (*p)(A) = overloaded; void* pointer = p; Note, though, that some underlying C implementations may prohibit converting a function pointer to a void*. What are "static member functions"? I seem to recall seeing something which related this term somehow to "operator new" and "operator delete" members. Are these two (special?) operator members implicitly static? Yes. Do "static" member functions (and operators) lack a "this" pointer? If so, is it an error to refer to "this" within the body of such operators. Yes to both questions. May a default argument be a non-constant expression? Yes. May an expression supplied as a default function argument contain a reference to: a) the "this" pointer (for member functions), or No. b) earlier parameters in the same parameter list, or No. c) later parameters in the same parameter list, or No. d) the address of the function for which the formal parameter list is being specified? If this means what I think it does, the answer is no -- how would you write the type of the argument? Are default argument expressions compile-time evaluated in the context of the default argument expressions themselves, or are they compile-time evaluated in the context of each individual call in which they are used? For instance, will the following print 3 or 7? const int k = 3; int f (int i = k) { printf ("%d\n", i); } int main () { const int k = 7; f (); } They're evaluated at run time at the point of call, but in the context in which they were written. In this example, f() is equivalent to f(3). Is it legal or illegal to put an "overload" statement inside a function? Inside a class? A struct? A union? Is it ever of any value to nest overload statements within these contexts? The `overload' declaration is obsolescent in 2.0 -- all functions are (potentially) overloaded. Is the following legal? overload x; overload x; overload x; Yes. Is the following legal? Is it ever required? overload operator+; Yes and no, respectively. Is the following legal? class base { public: overload member; void member (); void member (int i); }; Yes, but unnecessary even in older C++ implementations: member functions are always (potentially) overloaded. Is the following legal? int global_item; overload global_item; void global_item (int i); No -- you can only overload functions. Is there any possible ordering of the three statements above which would be considered legal? No. Is it ever legal to specify a return type for either a constructor or a destructor? Specifically, is it legal for constructors and/or destructors to return (a) void, or (b) void*, or (c) class_type*, where "class_type" is the name of the containing class? No. Is it legal or illegal to place a declaration for a member function within one "visibility section" (i.e. "public", "private", or "protected") of a class declaration and then to subsequently re-declare the same member function (with the same parameter list) in a different "visibility section" of the same class declaration, for example: class base { protected: void member (int i); public: void member (int i); // legal ? private: void member (int i) {} // legal ?? }; No. A member may be declared no more than once in a class declaration. It doesn't make sense to have more than one visibility for a single member, anyway. You can, however, have several overloaded member functions with the same name and different visibilities: class Foo { private: void f(int); public: void f(); }; Here, Foo::f() and Foo::f(int) are two different members and can have different visibilities. A fundamental principle is that changing visibility cannot affect the meaning of a program -- it can only affect whether or not the program is considered valid. -- --Andrew Koenig ark@europa.att.com