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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With