jimad@microsoft.UUCP (Jim ADCOCK) (09/20/90)
In article <8445@jarthur.Claremont.EDU> dfoster@jarthur.Claremont.EDU (Derek R. Foster) writes: |Is there any way to change the variable referenced by a reference variable |after the reference variable has been initialized? | |In particular, I have a class which is passed a reference to a target |variable which it is to modify. I would like to be able to simply |declare a class member variable which would be a reference to the |target variable, thus allowing my class member operations to access |the target variable as if it was simply a member variable. In other words, |I would like to be able to 'forget' that the target variable is actually |external to the particular class object which has been told to modify it, |except for in the routines which change to a new target. This seems like |a perfectly legitimate use of a reference variable, and would work quite |nicely if I was able to specify the target variable at the time a class |object was constructed. Unfortunately, I can't..... /**** In valid C++, references cannot be changed after being initialized. Compilers can perform significant optimizations on references, due to the fact that references cannot be aliased [they have no address, they can alias what they refer to, but cannot be themselves aliased] So, its not "legitimate" to try to "reassign" references via hackery. However, one can construct one's own reference class, that internally uses a pointer to store the reference, but presents that pointer to the outside world as if a reference. Then you just create an access member function to allow the internal pointer to be reassigned to refer to a new object. Unfortunately, when you try to do this, you'll find that while C++ allows pointer classes to overload operator->(), C++ ***does not*** allow reference classes the equivalent option of overloading operator.() To get around this problem, one is forced to provide a forwarding definition for all the public members of the class being referenced. This, in turn, generally prevents reference classes from being implemented via macros or templates. -- Please join me in lobbying the ANSI C++ committee to correct this oversight in the language definition. Overloading operator.() makes equal sense for reference classes as overloading operator->() for pointer classes. Classes that naturally follow value semantics need to be implementable using reference syntax, not pointer syntax, otherwise the usage becomes horrible, as you point out. -- The code below illustrates this problem, and the painful work-around. ****/ extern "C" { #include <stdio.h> } class THING { char* name; public: THING(char* nameT) : name(nameT) {} void DoSomething() { printf("%s DoSomething()\n", name); } void DoSomething2() { printf("%s DoSomething2()\n", name); } }; typedef THING* PTHING; typedef THING& RTHING; // To make the below classes generally useful, you'd probably want to // implement them as templates or macros, so that they can be parameterized // on the type of object being referred to.... // Yes! it's "trivial" to make a pointer class.... class THING_PTR { PTHING pthing; public: THING_PTR() { pthing = 0; } THING_PTR(RTHING rthing) { pthing = &rthing; } // ....because operator->() ***is*** overloadable. PTHING operator->() { return pthing; } RTHING operator*() { return *pthing; } operator PTHING() { return pthing; } void NowReferences(RTHING rthing) { pthing = &rthing; } // .... }; // But, is it trivial to make a reference class? .... class THING_REF { PTHING pthing; public: THING_REF() { pthing = 0; } THING_REF(RTHING rthingT) { pthing = &rthingT; } // ....but nooo, because operator.() ***isn't*** overloadable! // RTHING operator.() { return *pthing; } operator RTHING() { return *pthing; } void NowReferences(RTHING rthingT) { pthing = &rthingT; } // .... }; // So, to make a legitimate reference class one has to write a forwarding method // for every method in the referenced class -- which in turn means the // referencing class can't be implemented using macros or templates! class PAINFUL_THING_REF { PTHING pthing; public: PAINFUL_THING_REF() { pthing = 0; } PAINFUL_THING_REF(RTHING rthingT) { pthing = &rthingT; } operator RTHING() { return *pthing; } void NowReferences(RTHING rthingT) { pthing = &rthingT; } // .... // Now, add a forwarding call definition for ***ALL*** of THING's methods // ....In this case, just two, thank god.... void DoSomething() { pthing->DoSomething(); } void DoSomething2() { pthing->DoSomething2(); } }; main() { THING thing1("thing1"); THING thing2("thing2"); thing1.DoSomething(); thing2.DoSomething(); putchar('\n'); THING_PTR thingPtr(thing1); thingPtr->DoSomething(); thingPtr.NowReferences(thing2); thingPtr->DoSomething(); putchar('\n'); // Too bad, **someone** decided you can't overload operator, so you // can't do this: printf("Sorry: THING_REF doesn't work....\n"); printf(" ....because operator.() isn't implemented yet!\n\n"); #if 0 THING_REF thingRef(thing1); thingRef.DoSomething(); thingRef.NowReferences(thing2); thingRef.DoSomething(); putchar('\n'); #endif // -- Except, via the painful class implementation: PAINFUL_THING_REF thingRef(thing1); thingRef.DoSomething(); thingRef.NowReferences(thing2); thingRef.DoSomething(); putchar('\n'); }
rfg@NCD.COM (Ron Guilmette) (09/21/90)
In article <57570@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > >-- Please join me in lobbying the ANSI C++ committee to correct this oversight >in the language definition. Overloading operator.() makes equal sense for >reference classes as overloading operator->() for pointer classes... Finally, Jim and I have found something to agree on. :-) I'd say that Jim is correct is saying that treating either `.' or `->' as if they were OPERATORS (kinda like other operators) makes equal sense, i.e. *NONE*! Allowing overloading for either of these makes about as much sense as allowing overloading for `{' or `}'. Look folks, just because a particular C++ token contains some non- alphanumeric characters does not make it an operator! OK. Back to basics. What is `->'? Well, it is (syntactic sugar) shorthand notation for `*.' (i.e. dereference and select). So whenever I write: a->b (in C anyway) that's always equivalent to: (*a).b The meaning is clear and unambiguous. Apply the prefix unary dereference operator to the thing on the left, and then select out a member from the result. Now I've got no problem with (or objection to) allowing overloading for the unary prefix dereference operator BECAUSE THAT *IS* AN OPERATOR IN EVERY SENSE OF THE WORD. I do object (most violently) however to any attempts to call the selector `.' a `binary operator' (or to treat it as though it were one). Obviously, it isn't. For all of the binary operators that *I* know of, either the left or the right operands may be arbitrarily complex *expressions* (so long as these expressions evaluate to some type of value which is appropriate for the given category of operator). This is clearly *not* true in the case of the selector `.'. For the selector, the thing on the right *must* be a (qualified or unqualified) identifier which designates a some member belonging to the type of the thing on the left. The thing on the right *cannot* be an arbitrarily complex expression. It must be an identifier. Furthermore, the identifier *cannot* refer to any complete *object*. Rather, it must refer to a *member*. So the selector is *not* an operator. Period. Allowing overloading for it would make about as much sense as allowing overloading for `{' or `}' or `::' or `"'. Likewise, since `->' is just a shorthand notation for a combination of an operator (i.e. unary prefix dereference) and the selector, allowing overloading for `->' make as little sense as allowing it for the selector all by itself, i.e. none whatsoever. I can't understand why Bjarne and Jim and others have so much difficulty seeing the fundamental irrationality of allowing operator overloading to be done to things which are clearly not operators. If they really think that this is such a swell idea, then I challenge them to tell me (and everyone) why they are not suggesting allowing `operator{' to be overloaded. If there are any rules for what should be called an operator and what should not, and what should be overloadable and what should not, I'd like to see them! If these rules are at all consistant, if they make any sense whatsoever, and if they still would seem to permit -> to be overloaded, I'll eat my hat. -- // Ron Guilmette - C++ Entomologist // Internet: rfg@ncd.com uucp: ...uunet!lupine!rfg // Motto: If it sticks, force it. If it breaks, it needed replacing anyway.
ark@alice.UUCP (Andrew Koenig) (09/22/90)
In article <1677@lupine.NCD.COM>, rfg@NCD.COM (Ron Guilmette) writes: > In article <57570@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > > > >-- Please join me in lobbying the ANSI C++ committee to correct this oversight > >in the language definition. Overloading operator.() makes equal sense for > >reference classes as overloading operator->() for pointer classes... > Finally, Jim and I have found something to agree on. :-) > I'd say that Jim is correct is saying that treating either `.' or `->' as > if they were OPERATORS (kinda like other operators) makes equal sense, > i.e. *NONE*! Allowing overloading for either of these makes about as > much sense as allowing overloading for `{' or `}'. I can't resist this one. Of course . and -> aren't operators -- their right `operand' isn't an expression. However, one can view . or -> followed by an identifier as being a postfix unary operator. That is, writing a.b might be considered as applying the .b postfix operator to a. In other words, a.b could in principle be interpreted as a.operator .b() . I think this notation is consistent, at least in principle. It does, however, have a few problems: 1. How does one extend it to the -> symbol? Is p->b equivalent to (*p).operator .b()? Or is it necessary to define operator->b, which would be ambiguous (I think the first case is right)? 2. It is too easy to confuse operator.b() with operator b(). 3. (perhaps this should be 0.) Is it really necessary? I'm reluctant to propose new features of this sort because C++ has enough stuff already, so this is not a proposal. It's just a sketch of what it might mean to `overload the . operator' if it were really necessary to do it. -- --Andrew Koenig ark@europa.att.com
bobatk@microsoft.UUCP (Bob ATKINSON) (09/24/90)
In article <1677@lupine.NCD.COM> rfg@NCD.COM (Ron Guilmette) writes: >In article <57570@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: >> >>-- Please join me in lobbying the ANSI C++ committee to correct this oversight >>in the language definition. Overloading operator.() makes equal sense for >>reference classes as overloading operator->() for pointer classes... >OK. Back to basics. What is `->'? >Well, it is (syntactic sugar) shorthand notation for `*.' (i.e. >dereference and select). So whenever I write: This is true in C. Similar arguments apply in C to += -= <<= *= &=, etc. C++ is not C. See below. > a->b > >(in C anyway) that's always equivalent to: > > (*a).b > >The meaning is clear and unambiguous. Apply the prefix unary dereference >operator to the thing on the left, and then select out a member from the >result. > >Now I've got no problem with (or objection to) allowing overloading for >the unary prefix dereference operator BECAUSE THAT *IS* AN OPERATOR IN >EVERY SENSE OF THE WORD. > >I do object (most violently) however to any attempts to call the selector >`.' a `binary operator' (or to treat it as though it were one). Obviously, >it isn't. > >For all of the binary operators that *I* know of, either the left or the >right operands may be arbitrarily complex *expressions* (so long as these >expressions evaluate to some type of value which is appropriate for the >given category of operator). > >This is clearly *not* true in the case of the selector `.'. For the >selector, the thing on the right *must* be a (qualified or unqualified) >identifier which designates a some member belonging to the type of the >thing on the left. The thing on the right *cannot* be an arbitrarily >complex expression. It must be an identifier. Furthermore, the identifier >*cannot* refer to any complete *object*. Rather, it must refer to a >*member*. These last two sentences would ban pointers to members, which clearly exist today and are useful. >So the selector is *not* an operator. Period. Allowing overloading for >it would make about as much sense as allowing overloading for `{' or `}' >or `::' or `"'. Actually, the thing on the right hand side of a dot operator is precisely a "reference to member." References to members don't actually exist in C++ as defined, but they would bear the same relationship to "pointers to members" (which do exist) as a "reference to a non-member" bears to a "pointer to a non-member." I can elaborate in more detail if you or others would find that helpful. >Likewise, since `->' is just a shorthand notation for a combination of >an operator (i.e. unary prefix dereference) and the selector, allowing >overloading for `->' make as little sense as allowing it for the selector >all by itself, i.e. none whatsoever. Here is my understanding: Overloading of -> was put in to enable the creation of "smart pointers:" objects that clients use like pointers but in which the implementation programmer has control over the dereferencing operation. Such pointers have been found to be valuable in a number of contexts, which I will not explore here. Syntactically, there are two ways that a (normal) pointer can be dereferenced: with '*' and '->'. Writing smart pointers therefore requires that we be able to hook the dereferincing operation of both of these. I see only two ways of doing that: 1) declare that the use of -> is exactly the same as the use of (*). To hook dereferencing, a programmer need only implement the * operation, since this will be used by -> 2) give the programmer ways of hooking * and -> separately. (Both * and -> must be hookable; either you hook them in the same place, or you don't.) C++ has chosen 2). This is the more general of the two, since clearly you can do the same thing in each place. Moreover, 1) flies in the face of the C++ decision to separate the meaning of += from + and =, along with -=, *=, <<=, etc. I believe this was a sound decision, particuarly given the lack of ability to define new operators. >I can't understand why Bjarne and Jim and others have so much difficulty >seeing the fundamental irrationality of allowing operator overloading >to be done to things which are clearly not operators. I guess there is disagreement as to what is "clearly" an operator. >If they really think that this is such a swell idea, then I challenge them >to tell me (and everyone) why they are not suggesting allowing `operator{' >to be overloaded. The root of the difference I claim is that all operators (as so defined) are part of the expression language of C++, where { is solely related to the statement constructs. A more elaborate explanation would require an examination of the detailed syntactic structure of the language. >// Ron Guilmette - C++ Entomologist Bob Atkinson Microsoft
bdsz@cbnewsl.att.com (bruce.d.szablak) (09/25/90)
In article <1677@lupine.NCD.COM>, rfg@NCD.COM (Ron Guilmette) writes: > I can't understand why Bjarne and Jim and others have so much difficulty > seeing the fundamental irrationality of allowing operator overloading > to be done to things which are clearly not operators. Operator overloading is just syntatic sugaring to permit succinct and readable expression of the programmer's intent. Being able to overload the "->" construct works well with C++s user type system. I used it to implement a reference counting pointer type for example, and the code written using that type was much more readable as a result. That is a justification for permitting it to be overloaded. > If they really think that this is such a swell idea, then I challenge them > to tell me (and everyone) why they are not suggesting allowing `operator{' > to be overloaded. Off hand, I can't think of a user defined type that using '{' construct would result in more readable programs, but since it is used in the definition of initialized data I wouldn't discount that there is one... > If there are any rules for what should be called an operator and what should > not, and what should be overloadable and what should not, I'd like to see > them! If these rules are at all consistant, if they make any sense > whatsoever, and if they still would seem to permit -> to be overloaded, > I'll eat my hat. Prolog permits any legal identifier to be defined as an operator with a precedence and associativity. Other languages choose to do things in other ways. In the end, the "rules" are found in the manual. Perhaps "operator" is an unfortunate choice of terminology, but were stuck with it.
jimad@microsoft.UUCP (Jim ADCOCK) (09/25/90)
In article <1677@lupine.NCD.COM> rfg@NCD.COM (Ron Guilmette) writes: |In article <57570@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: |> |>-- Please join me in lobbying the ANSI C++ committee to correct this oversight |>in the language definition. Overloading operator.() makes equal sense for |>reference classes as overloading operator->() for pointer classes... | |Finally, Jim and I have found something to agree on. :-) | |I'd say that Jim is correct is saying that treating either `.' or `->' as |if they were OPERATORS (kinda like other operators) makes equal sense, |i.e. *NONE*! Allowing overloading for either of these makes about as |much sense as allowing overloading for `{' or `}'. I kind of agree, but I am not one for suggesting that "features" be removed from the C++ language. I am therefor one who suggests trying to make "features" of the language as self consistent as possible. Lacking overloaded op. forces a gratuitous lack of symmetry between usages of pointers and references. I think the best solution to the pointer verses reference syntax dilemma in C++ is just to give the user of the language the choice to use either in a given situation. If we had history to do over, it might have been better if compilers were allowed to apply . directly to pointers, implicitly dereferencing the pointer first, rather than introducing a separate -> syntax for pointers. However, that's water long gone under the bridge, and we need to try to make the best out of the situation today. If we can fully enable the use of references in everyday programming, then perhaps we can de-emphasize some of the alias problems with pointers that have never been resolved, and are re-rearing their ugly heads in C++. |Look folks, just because a particular C++ token contains some non- |alphanumeric characters does not make it an operator! Right. For that to happen the author[s] of the language has to say: "This particular token is an operator." Said comment has been made for "->", has not been made for "." I jump the gun in referring to "op.", because I am persuing an analogy to op-> -- an analogy that Ron seems to buy into. Does Ron seriously suggest that overloaded op-> be removed from the language? |OK. Back to basics. What is `->'? | |Well, it is (syntactic sugar) shorthand notation for `*.' (i.e. |dereference and select). So whenever I write: | | a->b | |(in C anyway) that's always equivalent to: | | (*a).b | |The meaning is clear and unambiguous. Apply the prefix unary dereference |operator to the thing on the left, and then select out a member from the |result. Likewise foo[n] is [was] "syntactic sugar" for *(foo+n). When operator overloading was first allowed in C++, the choice was made that the decision to keep or not keep the historical equivalences from C in the overloaded operators was up to the class programmer's discretion. Given that the appropriate "operator" overloadings are permitted, a class programmer can choose to copy these historical equivalences from C -- implicitly assuming that the C++ class user comes from a C background, and thus has these equivalences ingrained on their consciousness -- Or a class programmer can choose to ignore these historical meanings, possibly confusing and pissing off the "C" audience for those classes. But is it good to prohibit the operator overloadings necessary to enable a class programmer from making these "C" analogies, for fear that a class programmer might abuse these new found freedoms? |I do object (most violently) however to any attempts to call the selector |`.' a `binary operator' (or to treat it as though it were one). Obviously, |it isn't. Why not rather, allow me to call it what I like [perhaps you'd be happier if I call it a "rose?"] and argue for or against the feature based on its impact on C++ programmers, as opposed to its impact on C++ language lawyers? |For all of the binary operators that *I* know of, either the left or the |right operands may be arbitrarily complex *expressions* (so long as these |expressions evaluate to some type of value which is appropriate for the |given category of operator). When I was arguing for "op." by analogy to "op->", I was hoping that people would realize that both would be unary operators [as op-> is now] op-> is a member function of the type of its lhs, returning an object or primitive of type known at compile time, that the compiler can then reapply either an overloaded op-> to, or failing to find such, then perform the traditional compile-time binding to the rhs. |This is clearly *not* true in the case of the selector `.'. For the |selector, the thing on the right *must* be a (qualified or unqualified) |identifier which designates a some member belonging to the type of the |thing on the left. The thing on the right *cannot* be an arbitrarily |complex expression. It must be an identifier. Furthermore, the identifier |*cannot* refer to any complete *object*. Rather, it must refer to a |*member*. No more true nor less true than op-> |So the selector is *not* an operator. Period. Allowing overloading for |it would make about as much sense as allowing overloading for `{' or `}' |or `::' or `"'. "." is not an operator until it can be overloaded. It is then an operator. Like op->, it can make sense to turn "." into a unary operator, because it certainly has an object on the lhs to bind to. Other combinations of tokens with an object on one side, or the other, or both sides, could also be candidates for similar promotion to "operator" status, but I leave it to someone more flame resistant than myself to make such proposals, if they feel so motivated. In particular, it might be interesting to allow an extension to the language of: object1 -> object2 and object1 . object2 [IE allow binary overloading of op-> and op. where both the lhs, and the rhs are unambiguously objects, not field selectors] This would allow programmers a reasonable option for implementing message dispatch algorithms [such as hashed dispatch] other than that built into the C++ compiler, while hiding the exact mechanisms of such dispatch from the class user. [Alternately, the class programmer with a need for non- standard dispatch mechanisms must expose the class user to those dispatch mechanisms via non-standard method invocation syntax. Thus we're back to the standard dilemma: The overloading proponent says that this or that overloading is necessary so that I can appropriately encapsulate details of my class implementation. The overloading opponent says that each new overloaded feature of the language is yet another opportunity for hacks to write code that confuses and confounds] --But, this would entail larger changes to the language, and *I* am not making such a proposal. I am suggesting that allowing unary op. like unary op-> would fix a lot of the current syntax problems for users exposed to confusing mixtures of pointers and references with no apparent rhyme nor reason. |Likewise, since `->' is just a shorthand notation for a combination of |an operator (i.e. unary prefix dereference) and the selector, allowing |overloading for `->' make as little sense as allowing it for the selector |all by itself, i.e. none whatsoever. | |I can't understand why Bjarne and Jim and others have so much difficulty |seeing the fundamental irrationality of allowing operator overloading |to be done to things which are clearly not operators. Probably because if one looks at the problem from a different angle, the fundamental irrationality goes away. |If they really think that this is such a swell idea, then I challenge them |to tell me (and everyone) why they are not suggesting allowing `operator{' |to be overloaded. 1) Unlike op. , no one has presented a reasonable proposal why it might be good to allow doing so. 2) Unlike op. , one cannot argue that either the lhs or the rhs of "operator{" is an object such that the appropriate overloaded "operator{" can be selected. |If there are any rules for what should be called an operator and what should |not, and what should be overloadable and what should not, I'd like to see |them! If these rules are at all consistant, if they make any sense |whatsoever, and if they still would seem to permit -> to be overloaded, |I'll eat my hat. My proposal for such a set of rules is simply: 1) One or both sides of the proposed "operator" needs to be certainly an object, so that a member function can be unambiguously selected based on the type[s] of those object[s]. 2) Someone needs to make a strong argument for how allowing such to be an overloaded operator is going to solve real-world programming problems. 3) It better not cause wide-spread changes throughout the language.
bobatk@microsoft.UUCP (Bob ATKINSON) (09/26/90)
In article <57684@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: >In article <1677@lupine.NCD.COM> rfg@NCD.COM (Ron Guilmette) writes: >|Look folks, just because a particular C++ token contains some non- >|alphanumeric characters does not make it an operator! > >Right. For that to happen the author[s] of the language has to say: "This >particular token is an operator." Said comment has been made for "->", >has not been made for "." I jump the gun in referring to "op.", because >I am persuing an analogy to op-> -- an analogy that Ron seems to buy into. But see E&S, 13.4, pg 330: "The following *operators* cannot be overloaded: . .* :: ?: nor can the preprocessing symbols # and ## (Sect 16)." [Emphasis mine] >Likewise foo[n] is [was] "syntactic sugar" for *(foo+n). When operator >overloading was first allowed in C++, the choice was made that the >decision to keep or not keep the historical equivalences from C in the >overloaded operators was up to the class programmer's discretion. The reason that operators exist *at all* in C or C++ is for notational convenience. Clearly, the language could have used, say, a purely functional syntax, but this was deemed (correctly, in my opinion) to have been far too cumbersome. Therefore, convenient notation was invented to manipulate *the data types that were present in the language*. Of course, there are some relationships between the different notations. Examples of these include the relationships between -> and (*). and [] vs *(+). An important change occurred in C++. The programmer is now building *new* data types that he would like to manipulate with as much ease and simplicity as he can manipulate the built-in types. If good notations were important for the ease of use of C's types, then there is every reason to believe that they are important to the ease of use of programmer-defined types. Here is an exercise: Imagine you have an object which represents a range on a spreadsheet. Three very imporant operations that exist in today's macro languages are range construction (return a rectangular range from its upper left and lower right corners), range union, and range intersection. Today, Microsoft Excel uses ':', ',', and ' ' (space) respectively for these operations. Because of their frequency, it is *absolutely essential* that range construction and union be done in C++ with an operator. Functional notation (our only other alternative) is just too cumbersome. The desire for an operator for intersection lies in it symmetry w/ union. You have some constraints: precedence construction > precedence intersect > precedence union ranges (cells) also respond to arithmetic and comparision operators. Question: what C++ operators would you choose for these operations? (I believe there are two, maybe three, appropriate choices.) The point of this exercise is twofold: 1) to illustrate a user-defined data type for which notational convenience is very important, and 2) to illustrate the difficulty of providing that convenience given the limited choice of operators and their fixed precedences. There is very little reason that I can see for believing that the notation appropriate and efficient for manipulating the built-in data types of C will be appropriate and efficient for manipulating user-defined data types. There is even less reason to believe that the relationships between the existing notations will be appropriate for the built-in types. Sure, these relationships are a starting point for learning about operators, but when a programmer encounters an interface to a new data type that involves operators, he has to *realize* that unless the data tyep is just a number-like thing (such as Complex, Fraction, or Matrix might be), then the choice of operator notation for that class is a delicate exercise in compromise. *Many* of his preconceived notions about the semantics of operators will simply not apply. I personally believe that in the long term, this need for better notation for manipulating user-defined types will lead C++ to allow user-defined operators. In the mean time, given that we have to work with a fixed set of operators at fixed precedences and associativities, I believe that we'll have to make do as best we can. The more flexibility, the better job we can do of providing appropriate efficient notation. >|So the selector is *not* an operator. Period. Allowing overloading for >|it would make about as much sense as allowing overloading for `{' or `}' >|or `::' or `"'. > >"." is not an operator until it can be overloaded. It is then an operator. >Like op->, it can make sense to turn "." into a unary operator, because it >certainly has an object on the lhs to bind to. I disagree. I don't see the necessity of something being overloadable before being labled an operator. See below. >Other combinations of tokens with an object on one side, or the other, or >both sides, could also be candidates for similar promotion to "operator" >status, but I leave it to someone more flame resistant than myself to >make such proposals, if they feel so motivated. > >In particular, it might be interesting to allow an extension to the >language of: > >object1 -> object2 and >object1 . object2 > >[IE allow binary overloading of op-> and op. where both the lhs, and the rhs > are unambiguously objects, not field selectors] A neat idea. A particularly useful form for such a RHS happens in the case where object2 is of a particular enum type, say type IEnum. This provides the programmer with the ability to implement what look syntactically like data members but are implemented with a function for access. This *might* look something like: class C { enum IEnum { foo, bar, baz }; public: int operator.(IEnum); }; int C::operator.(anIEnum IEnum) { // return some integer } someClient { //... int anInt = someC.foo + someC.bar; //.. } Just a thought... >|If they really think that this is such a swell idea, then I challenge them >|to tell me (and everyone) why they are not suggesting allowing `operator{' >|to be overloaded. > >1) Unlike op. , no one has presented a reasonable proposal why it might be > good to allow doing so. > >2) Unlike op. , one cannot argue that either the lhs or the rhs of "operator{" > is an object such that the appropriate overloaded "operator{" can be > selected. I think there is a more simple answer. All the tokens currently described as operators can be found in strings derivable from "expression" as defined on pg 388 of E&S. { cannot be found in strings derived from expression. This is a fundamental distinction. >|If there are any rules for what should be called an operator and what should >|not, and what should be overloadable and what should not, I'd like to see >|them! If these rules are at all consistant, if they make any sense >|whatsoever, and if they still would seem to permit -> to be overloaded, >|I'll eat my hat. > >My proposal for such a set of rules is simply: > >1) One or both sides of the proposed "operator" needs to be certainly an > object, so that a member function can be unambiguously selected based on > the type[s] of those object[s]. > >2) Someone needs to make a strong argument for how allowing such to be > an overloaded operator is going to solve real-world programming problems. > >3) It better not cause wide-spread changes throughout the language. A cursory examination of 17.2 of E&S leads me to *conjecture* the following precise definition for the general notion of "operator." A operator is a token which is not an indentifier but which can be found in _some_ string legally derivable from the non-terminal "expression" in the grammar. (This definition must be modified to accomodate "parenthetical" operators. I shall not do that here. It merely pairs into one operator tokens which by this naive definition would be considered separate operators.) My brief examination indicates that all such tokens derivable from expression are today considered operators, and that conversely, all tokens considered operators today can be found in such strings. For this purpose, I will not consider a reserved word to be an identifier. This definition therefore encompasses the definition of "new", "delete", and "sizeof" as operators. Bob Atkinson Microsoft
tma@osc.COM (Tim Atkins) (09/27/90)
As I see it there is a very pragmatic reason for not allowing operator . in C++. Namely that it would be very difficult to distinquish normal use from the redefined use. For instance: class schitzy { int a; public: operator . () { /* do something different here */ } schitzy( schitzy& s){ a = s.a; // what do I really mean??? } }; I see no sane way to get back to the original operator . meaning in the above case. Any takers?
dfoster@jarthur.Claremont.EDU (Derek R. Foster) (09/30/90)
In article <3833@osc.COM> tma@osc.UUCP (Tim Atkins) writes: > > As I see it there is a very pragmatic reason for not allowing > operator . in C++. Namely that it would be very difficult to > distinquish normal use from the redefined use. > [example deleted for brevity] > I see no sane way to get back to the original operator . meaning > in the above case. Any takers? You might be able to use the global scope operator :: before the operator . when its use was ambiguous. i.e. object ::. member or, more verbosely, object ::operator. member I'm fairly certain the second is legal syntax. I'm not sure about the first. If a redefined version of operator.() existed, it would of course always override the default definition unless the global scope operator was specifically used. Just a side note about my letter which sparked this whole controversy: About the second or third option I tried in my search for a way to make my code work/look clean was the one of creating a "reference to variable" class, as a latter poster suggested. I still think that this would be a reasonable way to solve my problem, except for one minor detail: it doesn't work. As this same later poster pointed out, this basically can't be done due to C++'s inability to overload '.' It was nonetheless one of the first things I tried, and I was quite surprised to find out that it couldn't be done. This fact alone says something about the subject, I think. Refusing to allow operator.() to be overloaded seems an arbitrary restriction on the language, and forces programmers like me into very convoluted syntax to get around its absence. The lack of this function made me have to convert every reference to an object into pointer syntax, adding perhaps forty extra sets of (* ) into my program for no good reason, not to mention introducing some nasty bugs in the process. (Since the operator [] can be applied to a pointer to an object as well as to the object itself, any place where I forgot to phrase it as (*object)[] and instead just wrote object[] produced no compiler error, just some highly creative and sometimes very subtle results at runtime.) I wholeheartedly support adding overloading of operator.() to the language! Let's do it, folks! Derek Riippa Foster
jimad@microsoft.UUCP (Jim ADCOCK) (10/02/90)
In article <3833@osc.COM> tma@osc.UUCP (Tim Atkins) writes: | As I see it there is a very pragmatic reason for not allowing | operator . in C++. Namely that it would be very difficult to | distinquish normal use from the redefined use. For instance: | | class schitzy { | int a; | public: | operator . () { /* do something different here */ } | schitzy( schitzy& s){ | a = s.a; // what do I really mean??? | } | | }; | | I see no sane way to get back to the original operator . meaning | in the above case. Any takers? This "difficulty" is presumably what lead op. to not be allowed overloading in the first place. My proposal is that overloaded op. not be treated any differently than any other overloaded operator. Namely: if your code ever explicitly includes a dot with an instance of an overloading class on the lhs, you get the overloaded operator. In your example above, your use of dot gets you the overloaded version. How does one avoid getting the overloaded operator? By not using the dot. C/C++ provides synonyms that avoid using the dot: instead of: a = s.a; one can say: a = (&s)->a; Note, that like the other overloaded symbol operators, the compiler never implicitly calls the function behind your back -- the overloaded function only gets called when you write the symbol that is overloaded. So, in the important case of implicit this, you don't get the overloaded . operator -- anymore than you get the overloaded -> operator. int schitzy::aValue() { return a; } just returns the value of the local member. It doesn't implicitly call (*this).operator.().a; and it doesn't implicitly call this->operator->()->a; In the real domain of applicability of operator.() -- namely in "smart" reference classes -- these issues needn't arise in the first place. Consider writing a reference counted reference class: class FooRef { private: Foo* fooptr; public: FooRef(FOO& foo) : fooptr(&foo) { foo.incrementUsageCount(); } ~FooRef() { fooptr->decrementUsageCount(); } Foo& operator.() { return *fooptr; } Foo* operator&() { return fooptr; } // ... }; [[Note how much simpler and efficient reference counted references are compared to reference counted pointers]] I suspect that it would be difficult -- and truly schitzoid -- to try to write a class that acts simultaneously as a pointer and a reference. Anyone attempting such would get their just deserves.
dsouza@optima.cad.mcc.com (Desmond Dsouza) (10/02/90)
In article <57684@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: >... >"features" of the language as self consistent as possible. Lacking overloaded >op. forces a gratuitous lack of symmetry between usages of pointers and >references. >... >such a proposal. I am suggesting that allowing unary op. like unary op-> >would fix a lot of the current syntax problems for users exposed to confusing >mixtures of pointers and references with no apparent rhyme nor reason. I agree completely with Jim on this. The "confusing mixtures of pointers and references" can be somewhat remedied with an overloaded "." Assignable references are just one feature which could be implemented with this. -- Desmond. -- ------------------------------------------------------------------------------- Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324 Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza
ronb@burklabs (Ron Burk ) (10/07/90)
bobatk@microsoft.UUCP (Bob ATKINSON) writes: > Because of their frequency, it is *absolutely essential* that > range construction and union be done in C++ with an operator. > Functional notation (our only other alternative) is just > too cumbersome. Eeeeeeehaaaaaa! A man after my own heart! You will definitely be interested in the macro package I've designed for avoiding having to type those cumbersome C++ keywords. Here's the flavor: #define i if #define f for ... #define w while Next, I'm going to adapt this package to the "Hungarian" naming convention so that each keyword has the *type* of data it operates on encoded in its name! pipzyxw(/*condition*/) { /* stmts */ } ^ remember, "w" means WHILE !!! ==========> BIG :-)