12

I'd like to create a class that is associated to another class in some sort of parent-child relationship. For this the "child" class needs a reference to it's parent.

For example:

template <typename T>
class TEvent {
    private: T* Owner;
    public: TEvent(T* parent) : Owner(parent) {}
};

class Foo {
    private: TEvent<Foo> Froozle; // see below
};

Now the problem is that I can't initialize the Froozle instance directly, nor using the instanciation list of Foo's constructor, because this references are not allowed there. Apart from adding another method setParent(T*) (which I don't like too much because it means that I have to leave the TEvent<> instance in an invalid state), is there a way to achieve this?

sunside
  • 8,069
  • 9
  • 51
  • 74

5 Answers5

16

It is OK to use this in the initialization list, as long as it is not used to access any members that may not have been initialized yet.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • What he says. As long as the objects you pass `this` to "know" not to touch it during their construction (because the object `this` refers to isn't fully constructed at that moment), doing so is fine, even though some compilers (notably VC) emit a warning for it. – sbi Oct 23 '10 at 21:43
  • VC++ is the key point here. It works indeed. Seems like I have to suppress that "error" forcibly. Thanks alot! – sunside Oct 23 '10 at 21:53
  • 1
    I wish VC++ only emit warning when the member of "this" inside the ctor accessed. BTW, google::LogMessage class in [google-glog](http://code.google.com/p/google-glog) stores "this" to itself in mem-initializer list of ctor for debug purpose. I had to recompile with #pragma warning (4355 :disabled) to make it work in VC2010. – David Lee Feb 26 '13 at 19:10
12

From the standard 12.6.2/7 "Initializing bases and members" (emphasis mine):

Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified.

[Example:

class X {
    int a;
    int b;
    int i;
    int j;

public:
    const int& r;
    X(int i): r(a), b(i), i(i), j(this->i) {}
};

initializes X::r to refer to X::a, initializes X::b with the value of the constructor parameter i, initializes X::i with the value of the constructor parameter i, and initializes X::j with the value of X::i; this takes place each time an object of class X is created. ]

[Note: because the mem-initializer are evaluated in the scope of the constructor, the this pointer can be used in the expression-list of a mem-initializer to refer to the object being initialized. ]

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • Does this mean that "this" is only allowed in mem-initializer list of ctor or is it also allowed in the body of ctor ? I've tried using "this" inside the body of ctor in VC2010++ several times and it all compiles and runs fine with warning C4355 disabled but just because it works doesnt mean that I'm allowed to. Until now, I haven't found any in the [open-standard doc](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf) which states whether it's allowed to refer to "this" inside the body of ctor or not. – David Lee Feb 26 '13 at 19:21
  • @DavidLee: inside the constructor, `this` points to the object being constructed (just as in the initializer list as described above). You can definitely use `this` inside the constructor body. – Michael Burr Feb 26 '13 at 20:49
2

This is supposed to work; in fact,

template<class T>
class Child {
private:
    T *parent;
public:
    Child(T *parent) : parent(parent) {}
};
class Parent {
private:
    Child<Parent> child;
public:
    Parent() : child(this) {}
};

compiles fine for me with both g++ 4.4.5 and clang++ 2.8.

What is failing for you?

ephemient
  • 198,619
  • 38
  • 280
  • 391
  • The 'treat warnings as errors' compiler flag of VC and the MSDN documentation header that says "Error Message". :) You're absolutely right - It works, I just didn't see it. Thanks alot! – sunside Oct 23 '10 at 21:52
2

I don't think it's failing on you, unless you have the warning level set to 4 (or similar, I assume Visual Studio) and have enabled "treat warnings as errors".

Basically, this warning is A Good Thing, since it won't let you accidentally use the this pointer when what it points at is yet to be constructed.

However, when you know what you are doing wherever this is passed in the initialization list, the warning and error caused by this will be annoying.

You can get rid of it (again, assuming Visual Studio) by decorating the constructor (unless it's defined in the class declaration - then you must decorate all the class):

// warning C4355: 'this' : used in base member initializer list
#pragma warning (push)
#pragma warning (disable : 4355)
some_class::some_class()
: ...
{
}
#pragma warning (pop)
Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
1

If you're looking to suppress the warning, just do this:

class Foo
{
public:
    Foo() :
    Froozle(get_this())
    {}

private:
    Foo* get_this()
    {
        return this;
    }

    TEvent<Foo> Froozle; // see below
};

The indirection is enough to stop it.

GManNickG
  • 494,350
  • 52
  • 494
  • 543