Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 474
Itachi Uchiwa Avatar asked Apr 16 '21 00:04

Itachi Uchiwa


2 Answers

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.

like image 191
zkoza Avatar answered Oct 06 '22 00:10

zkoza


Is my program in Undefined Behavior?

No. The behaviour is well defined.

So why accessing the address of the member data A::x_ through a fully-qualified names yields an "Invalid" address?

You aren't using fully-qualified names. A fully qualified name always begins from the global namespace. For example, ::Foo::x_ would be a fully qualified name, while Foo::x_ it not.

You also aren't "accessing" the members (except when you insert x_). When you apply the unary operator & on a member name, the result is a pointer to data member. In the case of &Foo::x_, the type would be a int Foo::* i.e. pointer to member of Foo of type int.

Furthermore, 1 isn't necessarily "invalid" address, but what you are observing isn't an address at all. Character streams do not have a stream insertion operator that accepts a pointer to data member. They do however have an overload that accepts a bool and all pointers to data members are implicitly convertible to bool, and thus such overload is a valid candidate for overload resolution.

Since the pointer to data member in question isn't null, the converted value is true. When true is inserted to a character stream, the output is 1.

like image 26
eerorika Avatar answered Oct 06 '22 01:10

eerorika