Search code examples
c++compiler-errorsclanglanguage-lawyervariable-names

Variable with same name as type - which compiler is right?


In this code:

typedef int foo;

struct S
{
  foo foo;
};

int main() {}

all versions of clang -std=c++14 accept this code, however all versions of g++ -std=c++14 report:

5 : error: declaration of 'foo S::foo' [-fpermissive]
foo foo;
^
1 : error: changes meaning of 'foo' from 'typedef int foo' [-fpermissive]

Is the code correct?


Solution

  • According to the C++ standard, declaring a variable with the same name as a type is correct code in general, but invalid code within a class definition. The class case is specific, because names declared in a class definition are visible within the whole class definition, before and after the point of declaration of that name. In other scopes (global, namespace, function, ...) declared names are visible only after the point of declaration.

    For the example given in the question: If you have another reference to foo before the member declaration foo foo;,

    struct S { foo another_member; foo foo; };
    

    to which foo should it refer? To the type or the member foo? In this case it would be possible to deduce from the context that the type is meant, but the C++ standard probably avoided complexity for treating corner cases.

    But foo foo; is valid code outside of class definitions: Additionally to the excerpt from section [dcl.spec], which Serge Ballesta already citied in his answer, section [dcl.decl] is useful in this context. It gives a valid example for this case (in older versions of the C++ standard text in a note below the text, in later versions as part of the main text) - here from the N3242 draft (final draft for C++11):

    A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator. That is

    T D1, D2, ... Dn;

    is usually equvalent to

    T D1; T D2; ... T Dn;

    where T is a decl-specifier-seq and each Di is an init-declarator. The exception occurs when a name introduced by one of the declarators hides a type name used by the decl-specifiers, so that when the same decl-specifiers are used in a subsequent declaration, they do not have the same meaning, as in

    struct S ... ;

    S S, T; // declare two instances of struct S

    which is not equivalent to

    struct S ... ;

    S S;

    S T; // error

    The excerpt from section [dcl.spec] is useful as well, as it describes, when a name in a declaration of a variable is interpreted as type name and when as the variable name (long quote is given in Serge Ballesta's answer):

    ... it is interpreted as part of the decl-specifier-seq if and only if ...

    The relevant section for the class' case, which is given in the original question, was [basic.scope.class]/2 in C++17:

    ... A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule. ...

    This means that the code of the original question is invalid, but the compiler does not need to give an error. So both clang and gcc behave correctly (according to the C++ standard).