C++ For what types can the parameter type name be the same as the parameter name with a type specifier?

StackOverflow https://stackoverflow.com/questions/6092220

Question

This question is going to take a bit of explination, sorry. I am fixing an oversight in doxygen parsing some C++ code and I have come across an unusual corner case that doxygen doesn't account for. I have a fix but I want to make it more general so I need some explanation.

To illustrate the case where doxygen is failing I am going to define a contrived example involving The Simpsons (because that seems to be popular for these types of questions). Let's say that we have the following enum:

enum simpson { HOMER, MARGE, BART, LISA, MAGGIE };

Now we want to pass the enum value into a method (of the Simpsons class naturally) that looks like this:

const char* voicedBy(simpson simpson)
{
    switch (simpson) {
        case HOMER:
            return "Dan Castellaneta";
        case MARGE:
            return "Julie Kavner";
        case BART:
            return "Nancy Cartwright";
        case LISA:
            return "Yeardley Smith";
        case MAGGIE:
            return "*suck* *suck*";
    }
}

Unfortunately this produces a compiler error because the enum type 'simpson' is not allowed to be the same as the parameter name 'simpson' (Unlike in, say, C#). But, C++ has an answer to this. You put the enum keyword in front of the type name like this:

const char* voicedBy(enum simpson simpson)

and the code will now compile and run. Unfortunately doxygen doesn't account for this case and so it treats the entire string "enum simpson simpson" as a parameter type with no parameter name. I have come up with some code to fix doxygen in the case of an enum like above.

My question is, what other types is this kind of trick valid for? struct?, union?, typedef?, others? And for that matter, does the 'type specifier for method parameter with same name as parameter name' concept have a name so that I can get some more details on it?

Was it helpful?

Solution

What compiler and version are you using?

void foo( simpson simpson ) {}

No enum present, that is, you don't need to use an elaborated type specifier in this context, and it compiles perfectly in gcc 4.2 and 4.6. The problem is that inside the function, the argument name hides* the type, and if you want to declare a new variable with that type inside that scope you will need the elaborated type specifier, but in the function signature it is parsed left to right, and that means that the first simpson is the enum and at that time there is no collision. The second simpson introduces a local name, and from there on, simpson refers to the parameter and not the type.

void relationship( /*enum*/ simpson simpson, enum simpson other = HOMER );
//                   ^^^^ optional           ^^^^ required
{
   enum simpson yet_another = simpson;
// ^^^^ required              ^^^^^^^ first argument!
}

The same type of name hiding can happen if you define a function with the same name as the type you want:

void simpson();
void voicedBy( enum simpson s );
//             ^^^^ required

Note that if you add a typedef the things change a little in this last case: there will be a name clash between the typedef-ed name and the function name (or a variable name in the same scope).

* Here hides is not used in the sense of a variable in one scope hiding a variable with the same name in an outer scope. In C++, as in C, there are two identifier spaces, one for user defined types, and another for mostly everything else including typedef-ed type aliases. Lookup in C++ is performed from the inner scope to the outer scope, and in each scope the global identifier space is searched, if the identifier is not found, then the user defined types identifier space is searched for the same identifier. This last step is not performed in C, where elaborated type specifiers are required always. Only if that also fails, the compiler will move to the next scope.

OTHER TIPS

In C, the canonical name of a struct, union, or enum includes that prefix:

struct Point {
    int x, y;
};

enum Type {
    FIRST, SECOND, THIRD
};

struct Point p;
enum Type t;

Which is the source of the idiom of creating a typedef name with the prefix removed:

typedef struct Point {
    int x, y;
} Point;

typedef enum Type {
    FIRST, SECOND, THIRD
} Type;

struct Point p;
enum Type t;
Point p;
Type t;

C++ has this as well, with the same behaviour also given to class, and analogous behaviour given to template and typename in templates. However, it also removes the requirement of including the prefix except in ambiguous cases.

I didn't think this concept had a name, but I stand corrected: it's an elaborated type specifier. A suitable workaround for this may be to place the Doxygen comments on the declaration rather than the definition.

What you did there is the same thing C coders do all day - prefixing their user defined types with the appropriate keyword. The same works for struct, class, union, in typedefs, variable declarations, anywhere basically.

struct/class/union also. In the Standard, "elaborated-type-specifier"s, consisting of "class-key identifier", see 3.4.4-1. (BTW - if a switch case returns, it has no need to break.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top