this as lvalue?

Jonathan M Davis jmdavisprog at gmail.com
Fri Sep 3 14:16:56 PDT 2010


On Friday 03 September 2010 13:22:51 JMRyan wrote:
> I wouldn't have thought of "this" representing an lvalue.  However, the
> following absurdity compiles and executes flawlessly.  Just don't
> uncomment the assignment to y.i!
> 
> class Yikes
> {
>     int i;
>     this() { this = null; }
> }
> 
> 
> void main()
> {
>     auto y = new Yikes();
>     // y.i = 0;
> }
> 
> Is this a bug in the compiler (v.2.047)?  Am I missing something in
> thinking it shouldn't be?

Of course, this is an lvalue.

1. Think about a struct for a moment, rather than a class. It's a value type. 
Assigning to this inside a struct would change the struct's value. In some 
circumstances, it would make perfect sense to do this. Assigning it to the 
struct's init property to reset it would be a prime example.

2. Think about what a member function _really_ looks like. The member function 
printM() in this class here

class A
{
	int _x;

	void printMe()
	{
		writeln(x);
	}
}


really looks like this

void printMe(A this, printMe())
{
	writeln(this.x);
}


If you were to set this to null in printMe(), what would it do? Nothing. You 
just set a function parameter to null. Sure, after that, this be null _within 
that function_, but it shouldn't be null after that that because the actual 
object that this points to still exists, and when its reference gets passed to 
other member functions, those won't be null, because you just set the local 
variable to null. For instance, this program runs just fine:

import std.stdio;

class A
{
    int _x;

    this(int x)
    {
        _x = x;
    }

    void print()
    {
        writeln(_x);
    }

    void nullifyMe()
    {
        this = null;
    }
}

void main()
{
    auto a = new A(13);
    a.print();
    a.nullifyMe();
    a.print();
}


It prints

13
13


Now, _your_ program bombs. However, I believe that that's because you assigned 
null to this in the _constructor_. My guess is that internally, your constructor 
looks something like this:

Yikes this(Yikes this)
{
	this = null;
	return this;
}


When this is called, its member variables (namely i) have been initialized, and 
its initial state is passed to the constructor to further do whatever stuff that 
you want your constructor to do. It then likely return null, which is what you 
get from new() and is what is assigned to your local variable y. So, y is then 
null. The Yikes object that you just created still exists. It's floating around 
in memory. It's just that you can't get at it. If you try this instead:

import std.stdio;

Yikes global;

class Yikes
{
    int i;
    this() { global = this; this = null; }
}


void main()
{
    auto y = new Yikes();
    global.i = 1;
    writeln(global.i);
}


it works and prints 1. You could even get rid of the "auto y = " part.

So, this is most definitely an lvalue. It's a bit stupid to assign null to it - 
especially in the constructor - but it makes perfect sense that it works. And in 
some circumstances, it makes perfect sense to do so (though not with classes 
since all it would ever affect would be the local variable and any member 
functions that called from that member function).

- Jonathan M Davis


More information about the Digitalmars-d mailing list