通过限定名访问成员数据的地址会产生错误
Accessing the address of a member data through qualified name yields error
正如我们所知,派生 class public
和 protected
成员的直接或间接基础 class 可在该派生成员中使用。
所以我有这个例子:
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();
}
当我编译 运行 程序时,我得到输出:
0x7ffc2f7ca878 : 10
1 : 10
1 : 10
1 : 10
那么,为什么通过完全限定名称访问成员数据 A::x_
的地址会产生“无效”地址?但是直接访问它(非限定查找)是可以的。
我的程序是否存在未定义行为?
我使用 GCC 和 CLANG 编译了我的程序。
首先,很可能您不打算在每个 std::cout
语句中重复 &
运算符,所以我删除了每个重复的实例,而不是打印成员的值比它的地址。
现在看这个:
#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();
}
输出:
0x7fffe136c594 : 10
0x7fffe136c594 : 10
0x7fffe136c594 : 10
0x7fffe136c594 : 10
一切正常!我所做的更改是将 Foo::x_
之类的表达式括在括号中。为什么?因为 &Foo::x_
使用 运算符的内置地址 ,请参阅 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 Whosebug question 了解更多说明。
编辑
您可能想知道为什么 std::cout
显示 1
作为指向成员的指针。这是因为隐式转换为 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.
转换后的值当然是true
,因为你的指针是有效的,只有nullptr
(和0)被转换为false
。根据其他规则,true
转换为 1。Q.E.D.
Is my program in Undefined Behavior?
没有。行为定义明确。
So why accessing the address of the member data A::x_ through a fully-qualified names yields an "Invalid" address?
您没有使用完全限定的名称。完全限定名称始终从全局命名空间开始。例如,::Foo::x_
将是完全限定名称,而 Foo::x_
则不是。
您也不是在“访问”成员(插入 x_
时除外)。在成员名称上应用一元运算符 &
时,结果是指向数据成员的指针。在 &Foo::x_
的情况下,类型将是 int Foo::*
即指向类型 int
.
的 Foo
成员的指针
此外,1 不一定是“无效”地址,但您观察到的根本不是地址。字符流没有接受指向数据成员的指针的流插入运算符。然而,它们确实有一个接受 bool 的重载,并且所有指向数据成员的指针都可以隐式转换为 bool,因此这种重载是重载解析的有效候选者。
由于指向相关数据成员的指针不为空,因此转换后的值为真。当 true 插入字符流时,输出为 1.
正如我们所知,派生 class public
和 protected
成员的直接或间接基础 class 可在该派生成员中使用。
所以我有这个例子:
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();
}
当我编译 运行 程序时,我得到输出:
0x7ffc2f7ca878 : 10 1 : 10 1 : 10 1 : 10
那么,为什么通过完全限定名称访问成员数据 A::x_
的地址会产生“无效”地址?但是直接访问它(非限定查找)是可以的。
我的程序是否存在未定义行为?
我使用 GCC 和 CLANG 编译了我的程序。
首先,很可能您不打算在每个 std::cout
语句中重复 &
运算符,所以我删除了每个重复的实例,而不是打印成员的值比它的地址。
现在看这个:
#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();
}
输出:
0x7fffe136c594 : 10
0x7fffe136c594 : 10
0x7fffe136c594 : 10
0x7fffe136c594 : 10
一切正常!我所做的更改是将 Foo::x_
之类的表达式括在括号中。为什么?因为 &Foo::x_
使用 运算符的内置地址 ,请参阅 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 Whosebug question 了解更多说明。
编辑
您可能想知道为什么 std::cout
显示 1
作为指向成员的指针。这是因为隐式转换为 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.
转换后的值当然是true
,因为你的指针是有效的,只有nullptr
(和0)被转换为false
。根据其他规则,true
转换为 1。Q.E.D.
Is my program in Undefined Behavior?
没有。行为定义明确。
So why accessing the address of the member data A::x_ through a fully-qualified names yields an "Invalid" address?
您没有使用完全限定的名称。完全限定名称始终从全局命名空间开始。例如,::Foo::x_
将是完全限定名称,而 Foo::x_
则不是。
您也不是在“访问”成员(插入 x_
时除外)。在成员名称上应用一元运算符 &
时,结果是指向数据成员的指针。在 &Foo::x_
的情况下,类型将是 int Foo::*
即指向类型 int
.
Foo
成员的指针
此外,1 不一定是“无效”地址,但您观察到的根本不是地址。字符流没有接受指向数据成员的指针的流插入运算符。然而,它们确实有一个接受 bool 的重载,并且所有指向数据成员的指针都可以隐式转换为 bool,因此这种重载是重载解析的有效候选者。
由于指向相关数据成员的指针不为空,因此转换后的值为真。当 true 插入字符流时,输出为 1.