What if a reference is declared to point to itself? E.g. int& x = x;

Viewed 109

The following code compiles successfully in g++ version 10.1.0:

int main()
{
    int& x = x;
    return x;
}

The compiler even has a warning defined for this, suggesting it's intentional that it isn't an error. When compiled with -Wall, it returns the following output:

<stdin>: In function ‘int main()’:
<stdin>:3:14: warning: reference ‘x’ is initialized with itself [-Winit-self]
    3 |         int& x = x;
      |              ^
<stdin>:3:14: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
    3 |         int& x = x;
      |

My testing has shown that this results in a reference to a value at address 0, same as if it were declared as the int& x = *(int*)nullptr;. Naturally, this results in a segfault if the value referenced by x is ever used.

I can think of one (questionable) case where this might be useful: if you want to call a method on a class with no accessible constructor, when that method isn't static but nonetheless doesn't make any use of the class's data:

struct S {
    S() = delete;
    int value() { return 69; }
};

int main()
{
    S& s = s;
    return s.value();
}

But then there are other ways to do that, which don't even give warnings with -Wall and are less likely to be undefined behavior. (For example, ((S*)0)->value().)

So what I'm asking is: is this in fact undefined behavior? It certainly seems like something that would be. Is there any conceivable situation in which a self-reference is the best solution? And if not, why is the warning suppressed by default?

1 Answers

The reason that this code is accepted is because the C++ grammar allows for it; not because there is any practical reason for it.

In the C++ grammar, the first part of the definition introduces the name identifier which may be in in the rest of the scope, including in the same statement. This is what allows you to have code like:

int x = 1, &y = x;

The first int x produces the name x which may be used anywhere after it is seen. This has the side-effect of also allowing self-assignment, such as:

int x = x;

This assignment is, in fact, undefined behavior because x is not yet a initialized object, and is thus reading from uninitialized data (which is UB).

The same is true with a self-assigned reference; the int& x introduces the name x, whereas = x binds to a type that is bindable to a reference (which also happens to be int& x). This reference was not yet initialized by the time it was assigned, so this access is undefined behavior.

The compiler is free to treat undefined behavior however it chooses -- which it appears you are observing gcc treating the referenced address as 0x0. This is not guaranteed behavior, and may be influenced based on compiler version, optimization level, etc.


There is no real practical purpose for this; but it is easy to combat with using modern practices like auto, since code using auto in a self-construction will be unable to deduce the underlying type:

auto x = x;  // error, cannot deduce x
auto& y = y; // error, cannot deduce y

why is the warning suppressed by default?

The C++ standard only indicates a handful of errors that require diagnostic messages. Many errors fall under "no diagnostic required", which compilers may choose to add warnings for through optional flags. This is up to the implementation's discretion, and would be a question for the compiler's authors.

Most uninitialized access errors don't require diagnostics, which I am assuming this falls under.


As for your questionable-reason for using a self-reference: this would be undefined behavior, as is your example ((S*)0)->value().

Since the reference is constructed from UB, accessing any members from it is also UB.

For your other example, any form of member access on a null pointer is undefined behavior regardless of whether that member function accesses any member state, or whether it gets inlined. This doesn't mean it may not work with the right compiler flags on the right compiler; but it is not a portable, reliable, or safe solution.

Related