These articles are written by Codalogic empowerees as a way of sharing knowledge with the programming community. They do not necessarily reflect the opinions of Codalogic.

C++ Name Shadowing

By: Pete, April 2021

We all know that variable names in inner scopes can hide variables with the same name in outer scopes, thus making the variables in the outer scopes inaccessible. The technical term for this is "shadowing".

But variables can also have the same names as classes and functions. So how does this affect shadowing? If a variable, a class and a function all have the same name, which, if any, are accessible and which are not?

I wanted to find out.

I knew that the following code, wherein a variable is given the same name as a type (for example: S S;), is valid (and confirmed it using https://godbolt.org):

First Godbolt Example

But what would happen if I wanted a second variable of type struct S, so that I end of with code like this:

    struct S {};

    int main() 
    {
        S S;   // First Definition
        S S2;  // Second Definition
    }

Which, if any, of the definitions would be valid?

I set up a Twitter poll to see if I was the only one that didn't know:

First Twitter Poll

Reassuringly I wasn't alone.

To find the answer I asked Compiler Explorer again:

Second Godbolt Example

This shows that in the Second Definition the S in S S2; refers to the variable S declared in the First Definition. The variable, not the type, 'owns' the name S after the First Definition. Therefore, the Second Definition consisting of 'variable name' 'variable name' causes a compiler error.

So now I know the correct answer to the Twitter poll question was: "No".

As suggested by the compiler, if I want a second variable of type S, I need to write:

    int main() 
    {
        S S;
        struct S S2;
    }

But what happens if I also introduce a function called S? For example, what happens when I do the following:

void S() {}

struct S {};

int main() 
{
    S();
    S S;
}

I did another Twitter poll to see if I was the only one who didn't know:

Second Twitter Poll

And again, used Compiler Explorer to find the answer:

Third Godbolt Example

I get errors. Only the line S(); is valid. But has this anything to do with the order of the S(); and S S; lines? Let's flip them around and see:

Fourth Godbolt Example

This time, 3 errors/notes instead of 2.

In both cases the first 2 errors/notes indicate that the function name has priority over the type name. As before, if I want to use the type, I need to use struct S.

The third error of the latter example is because, although the compiler deemed the line S S; to be an error, it nonetheless decided to treat the name S as if it were associated with a variable. As this variable has no operator () it creates an error in the line S();.

This is shown more clearly in the following example where a variable named S is created of type int. The variable S is easily created, despite there being a function and a struct with the same name. But the variable doesn't have an operator () and so S(); causes an error.

Fifth Godbolt Example

And just to be sure a variable named S of type int is created, here's another example:

Sixth Godbolt Example

So what can be concluded from all this?

Firstly, the polls show that I wasn't the only one that didn't know what combinations are valid!

More importantly, a variable, a struct/class and a function can all have the same name.

If you have a struct and a function with the same name, the function has priority. If you want to use the name with a struct/class then you have to do struct S or similar.

Once you create a variable with the name, then this takes priority over any function or struct/class with the same name.

Are we done? Well no. The above analysis assumes a variable is created in function local scope. Foolishly I asked Compiler Explorer what would happen if I created a variable at global scope along side the struct and function.

These are the results:

Seventh Godbolt Example

It turns out I was unintentionally testing that names in inner scopes hide names in outer scopes. In the above example, the variable S conflicts with the function s(). Does it also conflict with the type struct S?:

Eighth Godbolt Example

No. As you can see, I can define a variable and a struct/class at the same global scope. But I can't define a variable and a function at the same global scope.

This does make sense because, in the case of the struct and variable, once defined, there are ways to disambiguate which one is wanted (by preceding the struct with the keyword struct if needed). But there is no similar way to disambiguate between referring to a variable and a function. Hence the language doesn't allow me to get into the trap in the first place.

So at global scope I can have a type with the same name as a function, or a type with the same name as a variable. But I can't have a function with the same name as a variable. Variables declared in inner scopes will not only hide variables at outer scopes, but also types and functions at outer scopes.

Admittedly you wouldn't intentionally write code like this, but there is a chance on a big project for names to clash by mistake. That could give you some odd errors that might be hard to debug. Knowing the above rules should help you understand and fix the problem.

Comments

Have comments on this article? Add them to the thread on Twitter.