10

I just found out about this:

static int x = x;

Why is this initialization accepted by the C++ compiler?

I would call it a compiler anomaly, but someone might come with a good explanation for this.

So for data with static storage, it is possible to initialize a variable with itself... I've tried this with a VS2015 and VS2017 compiler and also some other online C++ compilers.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Silviu
  • 909
  • 6
  • 6

2 Answers2

17

It's actually the same for static and non-static variables.

A name becomes visible immediately after its declarator and before its initialization, if any. Thus in

static int x = x;

the name x becomes visible immediately after its first occurrence, and can be referred to in the initializer. Since it's static, its initial value is well defined (it's 0).

This is also legal, even at block scope:

int x = x;

although here you're likely to get a warning because x is being initialized with its own indeterminate value (the behavior is undefined in most cases).

It's a silly thing to do, but C++ isn't in the business of going out of its way to prevent you from doing silly things. As an example, you might want to declare a pointer that points to itself:

void *p = (void*)&p;

Here the initializer refers to the address of p rather than its value, but the name p has to be visible to make that work. It wasn't considered worthwhile to add a special-case rule.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • 2
    `int x = x;` at block is UB in C++: evaluation of an indeterminate value (other than of unsigned char type) is immediate UB, there's no partial propagation of indeterminate value like in C11+DR451 – M.M Oct 20 '17 at 01:47
  • @M.M So what does `unsigned char x = x;` do, if it's not undefined? – Daniel H Oct 20 '17 at 02:26
  • 2
    @DanielH Same as `unsigned char x;`. See [basic.scope.pdecl] – M.M Oct 20 '17 at 02:36
  • There do exist cool use cases; that is, it has utility beyond even the small example given. Nice answer! – Dúthomhas Oct 20 '17 at 03:50
  • 2
    No need to *cast* to `void*`, just assign. – Deduplicator Sep 25 '18 at 13:31
  • @Deduplicator You're right. I know the conversion is done implicitly in C; I was less sure about C++. C permits implicit conversions to or from `void*` (to other object pointer types). C++ permits implicit conversions to `void*`, but not from `void*`. – Keith Thompson Sep 25 '18 at 17:51
0

If you wanted to prohibit all stupid constructs in C++ (and C too for that matter) you would get a pretty long list.

C++ is in practice a very different language compared to C, but it still have its roots there. And, at least in the beginning, the focus on being compatible with C was very strong. Even today the C++ standard includes C headers for that purpose.

So many of these strange things can be traced back to C, and C is a language that puts little demands on the compiler but more demands on the programmer. Many constructs yields undefined behavior or indeterminate value. There are two main reasons for this. First, it makes it easier to write the compiler correctly and having low system requirements. This was important in the early days of C. This language came 1972, which is 8 years before Commodore released the home computer VIC-20 with 4kB of memory. Secondly, it allows the compiler to produce faster code with less memory usage, which - as just mentioned - was very important in the past.

So even if there isn't any valid use case at all (I don't rule out the chance that it might be even if I cannot see it) there isn't really a strong enough reason to do something about it.

As Keith mentioned in his answer, if the variable is static, then it will have the value 0, which means that the value is not indeterminate. The result of static int x = x; is completely clear, so why forbid it? You would make the C++ specification bigger and harder to maintain and possibly break some old code, just to solve something that isn't a problem.

When it comes to non-static variables, it's a another thing because the value is indeterminate. Here there is a better reason to prohibit this construct, but on the other hand, you already get a warning for it. At least if you are using -Wall, which you should. I might also add that many warnings are warnings instead of error just to not break old code. But rather than forbidding initialization to itself, it would make more sense to prohibit usage of uninitialized variables completely.

klutt
  • 30,332
  • 17
  • 55
  • 95