16

I had a Q&A before: Point of declaration in C++. The rule point-of-declaration nicely is applicable on many situations. Now, I confused on usage of auto in combination of this rule.

Consider these two codes:

i. Declaring x by itself (we don't expect it to work):

{
  auto x = x;
}

ii. Declaring the inner x by the outer x (It makes error in gcc 4.8.x):

{
  int x = 101; // the outer x
  {
    auto x = x; // the inner x
  }
}

According to the rule of point-of-declaration, it should work but it doesn't. It seems there is another rule in the standard that I missed it. The question is, Where is the point-of-declaration when using auto?

 

There are two possibilities:

i. If the point of declaration is after =, at the end of statement:

auto object = expression;
                        ^
                        Is it here? If it is, why gcc complains?

So the second declaration is valid and must work, because there is no x but that outer one (which is declared before). Therefore auto x=x is valid and the inner x should be assigned to 101.

 

ii. If the point of declaration is before = :

auto object = expression;
           ^

Well, it doesn't make any sense because auto has to wait until see the following expression. For example auto x; is invalid.


Update: I need an answer which explains it by the rule point of declaration.

Community
  • 1
  • 1
masoud
  • 55,379
  • 16
  • 141
  • 208
  • 8
    `auto x = x` fails because `x` resolves to the local `x` (i.e. `auto x`) and yet the type of `x` hasn't been deduced yet. Just like how the `y` in `int y = y` resolves to the `int y`. – Simple Oct 02 '13 at 09:07
  • 2
    @Simple: My question is, why it resolves to the local. – masoud Oct 02 '13 at 09:07
  • see my edit. C++ has always done this so `auto` is consistent. – Simple Oct 02 '13 at 09:08
  • Because that's how C++ is defined to behave. When you do `auto x` the variable `x` is (partially) declared. However the type comes from the initialization where you recursively use `x` (which you just declared in the narrower local scope) but since it's not completely and fully declared yet (because it has no type) you get an error. – Some programmer dude Oct 02 '13 at 09:09
  • 1
    @Simple: Following the question [this](http://stackoverflow.com/q/15746271/952747). `auto x=x` when `x` is already defined should work. – masoud Oct 02 '13 at 09:10
  • 2
    I don't follow where the issue is. `x` is in scope after the `auto x` but it has no known type yet; attempting to use `x` as the initialiser causes an error because `x` has no type yet. – Simple Oct 02 '13 at 09:11
  • You should read that issue. If the `x` has no type yet, so there is no `x` in inner scope. Therefore there is just one `x`, the `x` of outer scope. Then, inner `x` can deduced by outer `x`. – masoud Oct 02 '13 at 09:14
  • Except it is in scope you just can't use its own name in its own initialiser. Note that `int x = x` is undefined behaviour which isn't a better alternative to `auto x = x` which just fails to compile. I did read the linked question but it just confirms what has already been said: `The point of declaration for a name is immediately after its complete declarator and before its initializer` so `x` is in scope. – Simple Oct 02 '13 at 09:16
  • 2
    @MM. "so there is no `x` in inner scope" is wrong. Just because it doesn't yet have a type doesn't mean it doesn't exist. –  Oct 02 '13 at 09:17
  • @hvd: Can an object exists without type in C++ as a static-type-language? – masoud Oct 02 '13 at 09:18
  • @MM. The object doesn't have to fully exist at this point, it just has to have a name associated. Which happens as soon as its declarator is parsed. – Angew is no longer proud of SO Oct 02 '13 at 09:20
  • At that point, `x` only exists as a name, not an object, but sure, as a name, it exists, as your question and the compiler's behaviour clearly shows. :) –  Oct 02 '13 at 09:20
  • It's point of __declaration__. When inner `x` is not declared yet. How it can have a name? – masoud Oct 02 '13 at 09:25
  • A definition is also a declaration. – Simple Oct 02 '13 at 09:27
  • @MM. On what are you basing your claim that `x` has not yet been declared? –  Oct 02 '13 at 09:30
  • @hvd: Because using `auto x = ...`. Compiler has not enough info to declare `x` and needs to see whatever is after `=`. When it looks at after `=`, it will see `... = x`. Now, It can simply deduce that `x` is the previous `x`. Then it can find out what type should be used for current declaring `x`. – masoud Oct 02 '13 at 09:32
  • 1
    @MM. Consider `class X; extern X x; class X { };` Even though `x` is only declared once, its declared type changes from an incomplete type to a complete type after the declaration. It's similar with `auto`. `x` has been declared by the time the `=` is seen, even though the type is not yet known. (I know it's not the best analogy, but I think it's close enough.) –  Oct 02 '13 at 09:33
  • So you want people to explain something to you using a rule that doesn't apply? – R. Martinho Fernandes Oct 02 '13 at 12:21
  • @R.MartinhoFernandes: In the worse case someone can say the rule doesn't apply on `auto` and forget the rule. Or he can expand the rule to cover `auto` mechanism and remind me what I missed. – masoud Oct 02 '13 at 12:27
  • @MM. since the name of the variable is forbidden from being in the initialiser expression (see existing answer), the rule doesn't apply. No other rule at all applies: only the one that says the program is ill-formed. Ill-formed programs are, by definition, programs that don't obey the rules of C++. – R. Martinho Fernandes Oct 02 '13 at 12:29
  • @R.MartinhoFernandes: The ambiguity is in that quote _"The name of the variable being declared..."_ the name is referring to the currently declaring name not the same name of another scope. So, someone can say well I can put the same name but from another scope and it's a valid non-ill-formed. Am I right? – masoud Oct 02 '13 at 12:46
  • 1
    Ok, that's a fair doubt. However, §3.1/8 says: "Two names are the same if — they are identifiers composed of the same character sequence". They are the same name. There is not "the currently declaring name" and "the name from another scope". There is only one name. That name may refer to different entities in different scopes, but its only one name. It doesn't even have to refer to any entity at all because that sentence only cares about the name. That's why point of declaration does not matter: it's irrelevant which entity is referred by that name; the standard simply forbids it. – R. Martinho Fernandes Oct 02 '13 at 13:05

4 Answers4

19
auto x = x; // inner x

is ill-formed.

To quote from the C++11 standard (emphasis mine):

7.1.6.4 auto specifier

...

3 Otherwise, the type of the variable is deduced from its initializer. The name of the variable being declared shall not appear in the initializer expression. ...

And so because x after = resolves to the x in auto x (as explained in the question you linked), that above piece of code is ill-formed.

Mark Garcia
  • 17,424
  • 4
  • 58
  • 94
  • 4
    Ah, good to see the Standard had the foresight to not just leave this to a byproduct of other rules. – Matthieu M. Oct 02 '13 at 09:36
  • I recently noticed in a cross-platform project, that the Microsoft Visual C++ Compiler (MSVC 2017) allows a inner "auto x(x)" declaration and initializes (as intended) the inner x with the value of the outer x. (no warnings at all). However, "auto x = x" produces a warning and undefined x. – Tom May 14 '18 at 20:50
10

Just like in any other kind of definition, the x on the right-hand side of the initialiser for auto x = x resolves to the local auto x. C++ has always done this (i.e. int x = x compiles but will give you undefined behaviour).

The reason auto x = x fails to compile is because while x is in scope it has no known type yet, and so using it as the initialiser fails because the type can't be deduced from the expression.

Just like any other kind of declaration, x is in scope after its declarator which is auto x.

int x = 10;
int y = 20;
{
    int x = x;  // This is NOT the outer x. This is undefined behaviour (reading an
                // uninitialised variable).
    auto y = y; // This is NOT the outer y. This is a compile error because the type of
                // y is not known.
}
Simple
  • 13,992
  • 2
  • 47
  • 47
5

Just adding an example with more explicit diagnostics:

auto ll = [&] { ll(); };

Results in (gcc):

error: variable ‘auto ll’ with ‘auto’ type used in its own initializer

or (clang):

error: variable 'll' declared with 'auto' type cannot appear in its own initializer
    auto ll = [&] { ll(); };
                    ^

You can see that there is an explicit rule for this. I haven't looked at the specs.

sehe
  • 374,641
  • 47
  • 450
  • 633
1

The compiler reads a whole statement (from the beginning of a line until the next semi-colon) and then evaluates the different parts of a statement using priorities of operations, and then when the time comes when the value of auto x is to be assigned, the type that came up after th = sign gets taken.

For example:

template <typename T>
T sum(T a, T b)
{
    return a+b;
}

int main()
{
    auto x = sum<double>(1,5); // x here is a double, because the return value is double
    auto y = sum<int>(1,7); //y is an int, because the return value is int
}

And about your auto x = x, you're redefining the same variable name. That's invalid! auto y = x shall work.

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189