[comp.std.c++] reference classes, and operator.

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');
}