10

The following C++ program compiles just fine (g++ 5.4 at least gives a warning when invoked with -Wall):

int main(int argc, char *argv[])
{
  int i = i; // !
  return 0;
}

Even something like

int& p = p;

is swallowed by the compiler.

Now my question is: Why is such an initialization legal? Is there any actual use-case or is it just a consequence of the general design of the language?

piripiri
  • 1,925
  • 2
  • 18
  • 35
  • 1
    Related [Does initialization entail lvalue-to-rvalue conversion? Is `int x = x;` UB?](https://stackoverflow.com/q/14935722/1708801) and [Has C++ standard changed with respect to the use of indeterminate values and undefined behavior in C++14?](https://stackoverflow.com/q/23415661/1708801) – Shafik Yaghmour May 31 '17 at 20:33
  • 1
    The `int i = i;` is supposed to suppress warnings about using uninitialized variables, but it uses an uninitialized variable to initialize the variable, which isn't actually an improvement. The `int &p = p;` looks most odd; I'm surprised that compiles at all. – Jonathan Leffler May 31 '17 at 20:35
  • Also see [Does this self-assignment do something sensible?](https://stackoverflow.com/q/42365507/1708801) I comment there [clang considers this idomatic way to silence unused variable warning](https://stackoverflow.com/questions/42365507/does-this-self-assignment-do-something-sensible/42365867#comment71916609_42365507) – Shafik Yaghmour May 31 '17 at 20:38
  • 1
    I see two questions here. (1) Why is `i` a defined variable when it appears in an initializer in the statement that defines `i`? and (2) Why does C++ allow an uninitialized variable to be used in an initializer (or, in any other place for that matter.)? – Solomon Slow May 31 '17 at 20:38
  • @JonathanLeffler Why shouldn't it compile? `p` is in scope for the initializer, just as `i` was for `int i = i;`. It causes undefined behaviour (binding a reference to an lvalue that does not denote an object) – M.M May 31 '17 at 21:26
  • @M.M: As I said, it looks odd to me — that means I don't know why it is compiling, but I'm not sure how to articulate it. Mainly, `p` is a reference to an `int`, which is not an `int`, so I would expect it to fail to compile, but I stand to be proved wrong by compilers or standards. I'll remain surprised that anyone would bother to think of trying it — I'll remain surprised that it is allowed at all by the standard — I won't be surprised to find that I'm simply not aware of a nuance of some sort that lets it through, – Jonathan Leffler May 31 '17 at 21:36
  • 1
    @JonathanLeffler well there's no rule specifically disallowing it; the same rules that allow `int &p = q;` and `int &r = p;` allow `int &p = p`, where `q` is an expression of type `int` and category *lvalue*. A variable is in scope as soon as its declarator is complete (i.e. before any initializer), and `int &p` means that `p` has type `int` and category *lvalue*. – M.M May 31 '17 at 21:38

3 Answers3

2

This is a side effect of the rule that a name is in scope immediately after it is declared. There's no need to complicate this simple rule just to prevent writing code that's obvious nonsense.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
1

Just because the compiler accepts it (syntactically valid code) does not mean that it has well defined behaviour.

The compiler is not required to diagnose all cases of Undefined Behaviour or other classes of problems. The standard gives it pretty free hands to accept and translate broken code, on the assumption that if the results were to be undefined or nonsensical the programmer would not have written that code.

So; the absense of warnings or errors from your compiler does not in any way prove that your program has well defined behaviour. It is your responsibility to follow the rules of the language. The compiler usually tries to help you by pointing out obvious flaws, but in the end it's on you to make sure your program makes sense.

And something like int i = i; does not make sense but is syntactically correct, so the compiler may or may not warn you, but in any case is within its rights to just generate garbage (and not tell you about it) because you broke the rules and invoked Undefined Behaviour.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
  • OK, but `int i=x;` is _not_ syntactically correct if `x` has not previously been declared, so why is the statement `int i=i;` treated as correct? I.e., what was the reason for making the declaration of `i` visible _inside_ the statement that declares `i`? – Solomon Slow May 31 '17 at 21:00
  • 1
    @jameslarge: `i` is complete (but uninitialized) at the end of `int i`; the `= i;` is referencing a defined (but uninitialized) variable. – Jonathan Leffler May 31 '17 at 21:04
  • 2
    It's logically equivalent to: int i; i=i; – Jeremy Friesner May 31 '17 at 21:18
  • 1
    @jameslarge One possible reason would be to allow initializing a variable with a return value of a static function from its own class: `LongClassName x = x.static_func();` instead of `LongClassName x = LongClassName::static_func()`. – HolyBlackCat May 31 '17 at 22:10
1

I guess the gist of your question is about why the second identifier is recognized as identifying the same object as the first, in int i = i; or int &p = p;

This is defined in [basic.scope.pdecl]/1 of the C++14 standard:

The point of declaration for a name is immediately after its complete declarator and before its initializer (if any), except as noted below. [Example:

unsigned char x = 12;
{ unsigned char x = x; }

Here the second x is initialized with its own (indeterminate) value. —end example ]

The semantics of these statements are covered by other threads:

Note - the quoted example differs in semantics from int i = i; because it is not UB to evaluate an uninitialized unsigned char, but is UB to evaluate an uninitialized int.

As noted on the linked thread, g++ and clang can give warnings when they detect this.


Regarding rationale for the scope rule: I don't know for sure, but the scope rule existed in C so perhaps it just made its way into C++ and now it would be confusing to change it.

If we did say that the declared variable is not in scope for its initializer, then int i = i; might make the second i find an i from an outer scope, which would also be confusing.

M.M
  • 138,810
  • 21
  • 208
  • 365