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*’ */