32

以前不知道 的存在std::addressof,为什么它存在对我来说是有意义的:作为在重载的情况下获取地址的一种方式operator&。然而,实现稍微不透明。来自gcc 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)));
}

reinterpret_cast<_Tp*>很明显。剩下的就是黑魔法了。有人可以分解这实际上是如何工作的吗?

4

4 回答 4

33
  • 首先你有__r哪个类型_Tp&
  • 它被reinterpret_cast'ed 到 achar&以确保以后能够获取其地址而不必担心operator&原始类型中的重载;实际上它被强制转换为,const volatile char&因为即使它们不存在reinterpret_cast也总是可以合法地添加constvolatile限定符,但如果它们存在,它不能删除它们(这确保了任何限定符_Tp最初具有,它们不会干扰强制转换)。
  • 这是const_cast'ed to just char&,删除限定符(现在合法!const_cast可以做reinterpret_cast关于限定符不能做的事情)。
  • 地址被占用&(现在我们有一个 plain char*
  • 它被reinterpret_cast'ed back to _Tp*(包括原始constvolatile限定符,如果有的话)。

编辑:由于我的回答已被接受,我将彻底补充并补充说,选择char作为中间类型是由于对齐问题,以避免触发未定义的行为。有关完整解释,请参阅@JamesKanze 的评论(在问题下)。感谢詹姆斯解释得这么清楚。

于 2013-04-24T14:54:45.030 回答
13

当您考虑它时,实际上非常简单,要在重载之前获取对象/函数的真实地址,operator&您需要将对象视为不同于它的真实存在的东西,某种不能具有重载运算符的类型。 . 一种内在类型(例如char)。

Achar没有对齐,可以驻留在任何其他对象可以驻留的任何地方,话虽如此;将对象转换为对 char 的引用是一个很好的开始。


但是在做的时候涉及到的黑魔法reinterpret_cast<const volatile char&>呢?

为了重新解释从实现中返回的指针,addressof我们最终将要丢弃限定符,例如constand volatile(最终得到一个普通的引用char)。这两个可以很容易地添加reinterpret_cast,但要求它删除它们是非法的。

T1 const a; reinterpret_cast<T2&> (a);

/* error: reinterpret_cast from type ‘...’ to type ‘...’ casts away qualifiers */

这有点像“比抱歉更安全”的把戏。“让我们添加它们,以防万一,我们稍后会删除它们。”


后来我们抛弃了限定符(constvolatile),const_cast<char&>最终得到一个对 的简单引用char,作为最后一步,这个结果变成了一个指向我们传递给实现的任何类型的指针。

这个阶段的一个相关问题是为什么我们没有跳过使用reinterpret_cast而直接进入const_cast? 这也有一个简单的答案:const_cast可以添加/删除限定符,但不能更改基础类型。

T1 a; const_cast<T2&> (a);

/* error: invalid const_cast from type ‘T1*’ to type ‘T2*’ */

它可能不像馅饼那么容易,但是当你得到它时它的味道肯定很好......

于 2013-04-24T15:04:20.403 回答
10

简短版本:

operator&不能为char. 因此,该类型被强制转换为char引用以获取保证为真实地址的内容。

const_cast由于 和 的限制,这种转换是在两次转换中完成的reinterpret_cast

更长的版本:

它正在执行三个连续的演员表。

reinterpret_cast<const volatile char&>

这实际上是强制转换为char&. constandvolatile仅存在因为_Tp可能是constor , volatileandreinterpret_cast可以添加它们,但无法删除它们。

const_cast<char&>

现在constandvolatile已被删除。 const_cast可以这样做。

reinterpret_cast<_Tp*>(&result)

现在获取地址并将类型转换回指向原始类型的指针。

于 2013-04-24T14:56:01.697 回答
6

由内而外:

  • 首先,它将__rtype 强制转换为 a const volatile char&:它强制转换为 achar&只是因为它肯定没有重载的类型operator&来做一些时髦的事情。之所以const volatile存在,是因为这些是限制,可以添加但不能删除reinterpret_cast_Tp可能已经是const和/或volatile,在这种情况下,这个演员阵容中需要一个或两个。如果没有,演员只是不必要地添加了它们,但它是为最严格的演员而写的。

  • 接下来,要带走const volatile你需要一个const_cast,这会导致下一部分const_cast<char&>......

  • 从那里他们只需获取地址并将其转换为您想要的类型,a _Tp*。请注意,_Tp可能是const和/或volatile,这意味着这些东西可以在此时添加回来。

于 2013-04-24T14:56:31.017 回答