Search code examples
c++memory-address

Accessing the address of a member data through qualified name yields error


As we know, a direct or indirect base class's of a derived class public and protected members are available in that derived one.

So I have this example:

struct Foo{int x_ = 10;};
struct Bar : Foo{};
struct FooBar : Bar{
    void f()const{
        std::cout << &x_ << " : " << x_ << '\n';
        std::cout << &Foo::x_ << " : " << Foo::x_ << '\n';
        std::cout << &Bar::x_ << " : " << Bar::x_ << '\n';
        std::cout << &FooBar::x_ << " : " << FooBar::x_ << '\n';
    }
};



int main(){
    FooBar fb{};
    fb.f();
}
  • When I compile and run the program I get the output:

      0x7ffc2f7ca878 : 10
      1 : 10
      1 : 10
      1 : 10
    

So why accessing the address of the member data A::x_ through a fully-qualified names yields an "Invalid" address? but accessing it directly (unqualified lookup) is OK.

  • Is my program in Undefined Behavior?

  • I've compiled my program using GCC and CLANG.


Solution

  • First of all, most likely you did not intend to repeat the & operator in each std::cout statement, so I've removed each repeated instance, to print the value of a member rather than its address.

    Now watch this:

    #include <iostream>
    
    struct Foo{int x_ = 10;};
    struct Bar : Foo{};
    struct FooBar : Bar
    {
      void f()const
      {
        std::cout << &x_ << " : " << x_ << '\n';
        std::cout << &(Foo::x_) << " : " << Foo::x_ << '\n';
        std::cout << &(Bar::x_) << " : " << Bar::x_ << '\n';
        std::cout << &(FooBar::x_) << " : " << FooBar::x_ << '\n';
      }
    };
    
    int main(){
      FooBar fb{};
      fb.f();
    }
    

    Output:

    0x7fffe136c594 : 10
    0x7fffe136c594 : 10
    0x7fffe136c594 : 10
    0x7fffe136c594 : 10

    Everything works just fine! What I changed was to enclose expressions like Foo::x_ in parenthesis. Why? Because &Foo::x_ uses the Built-in address-of operator, see Member access operators in cppreference.com. Its value is a pointer to data member, which is NOT an ordinary pointer, as it needs to be bound to an object in order to return an address. See also Similar stackoverflow question for additional explanation.

    EDIT

    You might wonder why std::cout displays 1 for your pointer-to-member pointers. Its because of the implicit conversion to bool:

    Boolean conversions
    A prvalue of integral, floating-point, unscoped enumeration, pointer, and pointer-to-member types can be converted to a prvalue of type bool.

    Quotation source

    The converted value is, of course, true, because your pointers are valid ones, and only nullptr (and 0) is converted to false. By other rule, true is converted to 1. Q.E.D.