c++ How to implement self referencing type

Drawing Hands is a lithograph by the Dutch artist M. C. Escher
Drawing Hands is a lithograph by the Dutch artist M. C. Escher

First of all, what is a “self-referencing type”? A type (struct or class) whose instance contains one or more references to the instance of the same type.

A sketch:

Why would anybody do this?

This is another question. After I implement this, I might give some valid use cases. But it turns out it is surprisingly non-trivial to implement this simple type in C++20. Hint: unlike pointers, for references one does not need to free the memory pointers are pointing to.

First (we all know, do we not?) one has to initialize the references as type (class or struct ) members.  Trying :

SR{}

As expected returns a compiler error response:

error: reference member of type 'const SR &' is uninitialized

I do oblige and create the next version

Trying to instantiate this struct, produces a somewhat cryptic message, at first glance:

default member initializer for 'ref' needed within the definition of enclosing class 'SR' outside of member functions

Much later it turned out this message was a clue leading to the solution. clang++ is known for its good and clear error messages. But this one is a definite head-scratcher.

Also, it has to be said, Visual Studio IDE has produced another but a bit clearer message (which is coming from the cl.exe used by it):

the generated default constructor cannot be used in an initializer for its own data member

Ok, that would be funny, but perhaps we need to initialize the ref member in a good old pre-modern c++98 way? Let’s try and do the next version.

Just to immediately realize the chicken-and-egg situation. To initialize the ref member we need to give it a reference to SR. But we are constructing the SR. So we need a reference to non-constructed SR? Or some such thing.

If ref would be a pointer that would be a nullptr. But the ref is a reference, not a pointer.  Outside of this problem context, this is very clear and easy to understand. Reference cannot exist without being initialized.

We surely all know this. But how to do this in the context of self-referencing type like SR here, is?

We need to initialize the SR reference member with the default SR instance before one exists.

Not to prolong this “essay” too much and to give you some useful C++ code let me jump to the end of the journey.

After trying to decipher that cryptic clang++ message and googling for an hour or two, or more, I realized I could initialize the ref with the instance of SR with the help of C++ “forward declaration”  AND the ‘extern’ keyword.

Almost immediately after this, I found the relevant page on  cppreference.com  about reference members. I should have looked into that before. It contains this solution too.

I have to admit clang++ message is cryptic but it gave me a vital clue indeed. After I wasted several hours.

And as it turns out, this issue was not standardized and solved before C++14.

You might find this example useful: Godbolt: https://godbolt.org/z/jE9f1f4WP .

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d