C++ 地址的实施
以前不知道存在C++ 地址的实施,c++,c++11,C++,C++11,以前不知道存在std::addressof,它存在的原因对我来说很有意义:作为一种在重载运算符&存在的情况下获取地址的方法。然而,实现稍微不透明一些。从合同通用条款第4.7.1条开始: template<typename _Tp> inline _Tp* __addressof(_Tp& __r) _GLIBCXX_NOEXCEPT { return reinterpret_cast<_Tp*> (&const_cast<char&>
std::addressof
,它存在的原因对我来说很有意义:作为一种在重载运算符&
存在的情况下获取地址的方法。然而,实现稍微不透明一些。从合同通用条款第4.7.1条开始:
template<typename _Tp>
inline _Tp*
__addressof(_Tp& __r) _GLIBCXX_NOEXCEPT
{
return reinterpret_cast<_Tp*>
(&const_cast<char&>(reinterpret_cast<const volatile char&>(__r)));
}
模板
内联*
__地址(\u Tp和\u r)\u GLIBCXX\u无例外
{
返回重新解释
(&const_cast(reinterpret_cast(__r)));
}
重新解释演员阵容是显而易见的。剩下的就是黑魔法。有人能详细说明这是如何工作的吗?- 首先是
,类型为\uu r
\u Tp&
- 它被
重新解释为
,以确保以后能够获取其地址,而不必担心原始类型中的重载字符&
;实际上,它被强制转换为运算符&
,因为const volatile char&
始终可以合法地添加reinterpret\u cast
和const
限定符,即使它们不存在,但如果它们存在,它无法删除它们(这确保了无论volatile
最初使用什么限定符,它们都不会干扰强制转换)\u Tp
- 这是
,只是const\u cast
,删除限定符(现在法律上,char&
可以做const\u cast
不能做的事情)reinterpret\u cast
- 地址为
(现在我们有了一个普通的&
)字符*
- 它被重新解释回
(包括原始\u Tp*
和常量
限定符(如果有)易失性
Edit:由于我的答案已被接受,我将详细说明,选择
char
作为中间类型是由于对齐问题,以避免触发未定义的行为。请参阅@JamesKanze的评论(在问题下)完整的解释。感谢詹姆斯解释得如此清楚。简短版本:
运算符&
不能为char
重载。因此,类型将被转换为char
引用,以获得保证为真实地址的内容
由于const\u cast
和reinterpret\u cast
的限制,转换分两次进行
较长的版本:
它正在进行三次连续的投射
reinterpret_cast<const volatile char&>
现在,const
和volatile
已被删除。const\u cast
可以这样做
reinterpret_cast<_Tp*> &(result)
reinterpret\u cast&(结果)
现在,地址被获取,类型被转换回指向原始类型的指针。由内而外:
- 首先,它将
类型强制转换为\r
:它强制转换为常量volatile char&
,只是因为它确实没有重载的char&
来做一些奇怪的事情。之所以存在运算符&
,是因为这些是限制,它们可以添加,但不能删除重新解释强制转换常量volatile
可能已经是\u Tp
和/或const
,在这种情况下,此强制转换需要一个或两个。如果没有,则强制转换只是不必要地添加了它们,但它是为最严格的强制转换编写的volatile
- 接下来,要去掉
您需要一个常量volatile
,这将导致下一部分…常量cast
常量cast
- 他们只需从那里获取地址并将其转换为您想要的类型,即
。注意\u Tp*
可能是\u Tp
和/或const
,这意味着这些内容可以在此时添加回来volatile
操作符之前获得对象/函数的真实地址&
你需要将对象视为一种非真实的东西,一种不能有重载操作符的类型..一种内在类型(例如char
)
char
没有对齐方式,可以驻留在任何其他对象可以驻留的任何位置,也就是说,将对象强制转换为对char的引用是一个非常好的开始
但是在重新解释施法时所涉及的黑魔法是什么呢 为了从
addressof
的实现中重新解释返回的指针,我们最终希望放弃限定符,例如const
和volatile
(以简单的引用char
结束)。这两个可以通过reinterpret\u cast
轻松添加,但要求它删除它们是非法的
T1 const a; reinterpret_cast<T2&> (a);
/* error: reinterpret_cast from type ‘...’ to type ‘...’ casts away qualifiers */
这可能不像馅饼那么容易,但当你得到它的时候,它的味道确实很好。用
char&
(或char*
)避免破坏严格的别名规则,并且对于内置类型,例如char
,不能重载运算符和。因此,您最终会得到一个指向\u Tp
@Praetorian的基地址的指针。这里不存在别名规则问题,因为您不取消引用结果。使用char
的原因是它无法ot有对齐要求;如果您使用了int
,并且int
的对齐比\u-Tp
更严格,则转换可能会更改实际地址。@当然,JamesKanze Ahh没有考虑不读取演员结果。幸好我发布了一条注释而不是答案:)@使用转换结果的syam(除非是字符类型)是未定义的行为,因此实现可以做任何它想做的事情。在字节寻址机器上,reinterpret\u cast
转换通常在机器代码级别不起任何作用。然而,在字寻址机器上,int*
通常比char*
小;char*T1 const a; reinterpret_cast<T2&> (a);
/* error: reinterpret_cast from type ‘...’ to type ‘...’ casts away qualifiers */
T1 a; const_cast<T2&> (a);
/* error: invalid const_cast from type ‘T1*’ to type ‘T2*’ */