简体   繁体   中英

Why compiler generate a warning about shadowing when we declare the variable with the same name as the member variable of base class?

When I add -Wshadow flag, compiler (gcc 12.1.0) generates a following warning for the below code:

prog.cc: In member function 'void Derived::myfunc()':
prog.cc:16:13: warning: declaration of 'i' shadows a member of 'Derived' [-Wshadow]
   16 |         int i = 3;
      |             ^
prog.cc:8:9: note: shadowed declaration is here
    8 |     int i{2};
      |       
class Base
{
private:
    int i{2};
};

class Derived : public Base
{
public:
    void myfunc(void)
    {
        int i = 3;
        std::cout << i << "\n";
    }
};

What I am thinking about it is: because we cannot use just "i" variable inside of myfunc (because it is compile time error), it is unnecessary to raise such a warning.

Do you have any idea about why compiler gives a warning in this case?

Shadowing is about name lookup, not access rules

It's by design, particularly not taking into account access rules but rather shadowing via how it affects lookup results. From GCC's description :

 -Wshadow

Warn whenever a local variable or type declaration shadows another variable, parameter, type, class member (in C++), or instance variable (in Objective-C) or whenever a built-in function is shadowed. Note that in C++, the compiler warns if a local variable shadows an explicit typedef, but not if it shadows a struct/class/enum. If this warning is enabled, it includes also all instances of local shadowing. This means that -Wno-shadow=local and -Wno-shadow=compatible-local are ignored when -Wshadow is used. Same as -Wshadow=global.

That the shadowed i is a class member via inheritance and moreover private is orthogonal to the fact that it is being shadowed in the sense of what name lookup will find.

Example

As an example as to why this could be problematic, consider the following delta, which introduces a bug in the a contrived program.

Program at time N :

struct A { 
    // Use 0 to indicate that "all is good"
    static constexpr int kStatusIsGood = 0;
    int status{}; // zero-initializer to 0, oops
};

struct B : A {
    constexpr bool clientIsGood(int status) {
        // warning: declaration of 'status' shadows a member of 'B' [-Wshadow]

        (void)status; // logging, using it somehow

        // Key result of the function
        return status == kStatusIsGood;
    }
};

constexpr int kClientPanic = 42;
static_assert(!B{}.clientIsGood(kClientPanic));

Program at time N+1

Where a dev has changed the name of the status parameter but forgot to update the function body, which silently passes compilation due to a shadowed variable now being found by lookup:

struct A { 
    // Use 0 to indicate that "all is good"
    static constexpr int kStatusIsGood = 0;
    int status{}; // zero-initializer to 0, oops
};

struct B : A {
    constexpr bool clientIsGood(int client_status) {       
        (void)client_status; // logging, using it somehow

        // Ups, a bug was left here
        return status == kStatusIsGood;
    }
};

constexpr int kClientPanic = 42;
static_assert(B{}.clientIsGood(kClientPanic));  // "all is good :)"

An i variable from the base class is in the existing derived class. A variable with the same name is defined again in the function. The name search is terminated as soon as the function is in scope. That's why the variable in the base class is not used and is stated to shadow it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM