Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 606
M.M Avatar asked Dec 24 '15 09:12

M.M


3 Answers

The code is wrong. typedef is a new name for a existing type. So you can not create a variable with a type's name like foo foo; is equal to int int.

g++ -std=c++14 is the correct one.

Also refer this question

like image 62
Embedded C Avatar answered Nov 10 '22 12:11

Embedded C


I would say CLang is correct here - even if I would never use this.

C++ 14 draft N4296 says in 7.1 Specifiers [dcl.spec] 3

If a type-name is encountered while parsing a decl-specifier-seq, it is interpreted as part of the decl-specifier- seq if and only if there is no previous type-specifier other than a cv-qualifier in the decl-specifier-seq. The sequence shall be self-consistent as described below. [ Example:

typedef char* Pc;
static Pc; // error: name missing

Here, the declaration static Pc is ill-formed because no name was specified for the static variable of type Pc.

To get a variable called Pc, a type-specifier (other than const or volatile) has to be present to indicate that the typedef-name Pc is the name being (re)declared, rather than being part of the decl-specifier sequence.

For another example,

void f(const Pc); // void f(char* const) (not const char*)
void g(const int Pc); // void g(const int)

(emphasize mine)

Even if an example is not normative, it let think that for the writers of C++ specification, a variable can redeclare the name of a typedef.

But g++ is simply more conservative what looks more reasonable. If I ever see such a construct in production code, the programmer would soon learn not to do it again, even I the compiler accepted it...

like image 31
Serge Ballesta Avatar answered Nov 10 '22 11:11

Serge Ballesta


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, is [basic.scope.class]:

... 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).

like image 2
Norbert Galm Avatar answered Nov 10 '22 11:11

Norbert Galm