Does a declaration using "auto" match an extern declaration that uses a concrete type specifier?

C++Language LawyerExternAuto

C++ Problem Overview


Consider the following program:

extern int x;
auto x = 42;
int main() { }

Clang 3.5 accepts it (live demo), GCC 4.9 and VS2013 do not (live demo for the former). Who is right, and where is the correct behavior specified in the C++ Standard?

C++ Solutions


Solution 1 - C++

There's surprisingly little in the standard about this. About all we hear about redeclaration is:

> [C++11: 3.1/1]: A declaration (Clause 7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. [..]

and the only relevant part of auto's semantics:

> [C++11: 7.1.6.4/3]: Otherwise, the type of the variable is deduced from its initializer. [..]

(reminding us that the type of x is int).

We know that a variable must be given the same type by all declarations:

> [C++11: 3.5/10]: After all adjustments of types (during which typedefs (7.1.3) are replaced by their definitions), the types specified by all declarations referring to a given variable or function shall be identical, except that declarations for an array object can specify array types that differ by the presence or absence of a major array bound (8.3.4). A violation of this rule on type identity does not require a diagnostic.

and the "after all adjustments of types" ought to take care of any questions regarding auto's participation in all of this; my interpretation, then, is that this is inherently a valid redeclaration (and definition) of the x at global scope with type int, and that Clang is correct. Even if we propose that auto does not count as "adjustment of type", since no diagnostic is required, at worst all listed implementations are compliant in their own way.

I believe GCC and Visual Studio are taking the following as inspiration:

> [C++11: 7.1.6.4/5]: A program that uses auto in a context not explicitly allowed in this section is ill-formed.

…but I think that this is short-sighted. It seems unlikely that the standard language is intended to prohibit the usual redeclaration rules, just because they are not repeated or explicitly referenced from within 7.1.6.4.

C++14 adds wording that relates to declarations of functions with deduced types:

> [C++14: 7.1.6.4/13]: Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type. [..]

By symmetry one might suggest that, in your int case, it is intended that GCC and VS be correct in rejecting the program. However, this is a different feature (since deduction cannot be applied to mere declarations) and thus a different scenario.

Either way, improved standard wording would help here. I consider it a [reasonably minor] editorial defect.

Solution 2 - C++

###Note###

I answered a question that was closed a duplicate of this one. I asked for merge and was told instead to provide an answer here. See below for my original answer.

###Update clang is correct###

I asked this question on twitter and the response I received from Richard Smith was as follows:

>Not a defect, it's intentional that this restriction applies only to deduced return types and not to variable types. For variables, it's just a convenience shorthand, but return type deduction affects something more fundamental about functions (and especially function templates).

So the logic is that this is allowed by [dcl.spec.auto] and to restrict this for deduced return types paragraph [dcl.spec.auto]p11 was added to the section. Otherwise there is no restriction and therefore this is not restricted for the variables case.

###Original###

Currently [dcl.spec.auto] does not seem to cover this case explictly but it does say in [dcl.spec.auto]p5:

>A program that uses auto or decltype(auto) in a context not explicitly allowed in this subclause is ill-formed.

and we can see it makes a similar case for functions ill-formed in [dcl.spec.auto]p11:

> Redeclarations or specializations of a function or function template > with a declared return type that uses a placeholder type shall also > use that placeholder, not a deduced type. Similarly, redeclarations or > specializations of a function or function template with a declared > return type that does not use a placeholder type shall not use a > placeholder. [ Example: > > auto f(); > auto f() { return 42; } // return type is int > auto f(); // OK > int f(); // error, cannot be overloaded with auto f() > > ....

So although this could use clarification as currently worded it feels like gcc is correct and this is ill-formed.

Solution 3 - C++

I'd imagine the restriction in [dcl.spec.auto]p11 exists because otherwise, that would allow:

int f();
auto f(); // What's the return type here?

The thing is, you can have an undeduced type type has the return type of a function. There are no deduction rules based on previous declarations, which is why such mixing is disallowed for functions, even though the following would be perfectly fine:

int f();
auto f() { return 1; }

This problem does not exist for variables:

extern int v;
extern auto v; // ill-formed

Any declaration-only variables has to use a non-placeholder type. What this means is that if you use a placeholder type for the definition of v, it can get deduced without any problems and then of course has to match the non-placeholder type used in the first declaration.

extern int v;
auto v = 1; // ok, type deduced as 'int', matches first declaration.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndy ProwlView Question on Stackoverflow
Solution 1 - C++Lightness Races in OrbitView Answer on Stackoverflow
Solution 2 - C++Shafik YaghmourView Answer on Stackoverflow
Solution 3 - C++Rakete1111View Answer on Stackoverflow