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

3

16

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?

Universe answered 24/12, 2015 at 9:5 Comment(5)
I would say GCC is correct hereSirmons
As the diagnostic states, you can force this to compile by using the -fpermissive switch... but why would you do that rather then fix the code!? I'd rather get an error that have it quietly accepted, hiding future maintenance problems.Exit
@Clifford, with -fpermissive the error is converted to warning. But the problem remains. Put this statement in any class method and it results in error: auto x = foo(5); Moreover, I think the OP isn't introducing such naming deliberately in the code. Mostly it's a curiosity question, which even I had many years back regarding this exact topic. That's why reopened this question.Tenotomy
Possible duplicate of typedef changes meaningWedekind
Visual studio 2015 also accept this codeRidgeling
R
7

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

Runnerup answered 5/5, 2022 at 15:49 Comment(1)
The relevant section is [basic.scope.class]/2 in C++17; however that text is not present in later drafts, so it would be great to hear some further analysis. (e.g. was this rule rescinded for C++20, or encoded in different wording)Universe
F
4

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

Fenny answered 25/1, 2016 at 12:50 Comment(1)
This is not a good answer, because it is actually legal to go foo foo; at block scope. There is a rule against it specifically for the case of being inside a class definition. The reason int int; is not allowed is because keywords cannot be variable names.Universe
C
1

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

Cancer answered 25/1, 2016 at 13:39 Comment(2)
Think from a program reader's point of view. You can find g++ as a correct one. As you see any variable declaration as foo foo ! will be annoying them. Using of variable foo is also difficult. That's why using a common name for two things is not the best always not only C even in real world.Fenny
@LetUsEmbed: I do agree with you, no programmer should ever write such code! The question I answered is: what does the standard says on that code?Cancer

© 2022 - 2024 — McMap. All rights reserved.