执行地址

时间:2013-04-24 14:46:26

标签: c++ c++11

之前一直没有意识到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 个答案:

答案 0 :(得分:30)

  • 首先,__r类型为_Tp&
  • reinterpret_castchar&,以确保能够稍后获取其地址而不必担心原始类型中的operator&过载;实际上它被转换为const volatile char&因为reinterpret_cast总是可以合法地添加constvolatile限定符,即使它们不存在,但如果它们存在则无法删除它们(这确保了_Tp最初的限定符,它们不会干扰演员表。)
  • const_cast仅限于char&,删除限定符(现在合法!const_cast可以执行reinterpret_cast无法对限定符执行的操作)。
  • 地址取&(现在我们有一个普通的char*
  • reinterpret_cast已追溯至_Tp*(其中包括原始constvolatile限定符(如果有)。

编辑,因为我的答案已经被接受,我会彻底并补充说char作为中间类型的选择是由于对齐问题导致的,以避免触发Undefined行为。有关完整说明,请参阅@ JamesKanze的评论(在问题下)。感谢詹姆斯如此清楚地解释它。

答案 1 :(得分:12)

当你想到它时,它实际上非常简单,为了在一个过载的operator&的前提下获得一个对象/函数的真实地址,你需要将该对象视为一个非实际的东西,一些不能有重载运算符的类型.. 内在类型(例如char)。

char没有对齐,并且可以驻留在任何其他对象可以的任何位置,具有该说法;将对象转换为对char的引用是一个非常好的开始。


但是在执行reinterpret_cast<const volatile char&>时会涉及黑魔法

为了重新解释来自addressof实现的返回指针,我们最终会想要放弃constvolatile等限定符(最终结果)用简单的引用char)。可以使用reinterpret_cast轻松添加这两个,但要求删除它们是非法的。

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

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

这是一个“更安全而不是抱歉”的技巧.. “我们添加它们,以防万一我们将在以后删除它们。”


稍后我们使用const_cast<char&>抛弃限定符( const volatile ),最后得到char的简单引用,此结果是,作为最后一步,转回指向我们传入实现的任何类型的指针。

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

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

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

这可能并不容易,但是当你得到它时它确实味道很好..

答案 2 :(得分:8)

简短版本:

operator&无法为char重载。因此,类型被强制转换为char引用,以获得保证为真正地址的内容。

由于const_castreinterpret_cast的限制,转化是在两次演员表中完成的。

版本较长:

它正在进行三次连续演员表演。

reinterpret_cast<const volatile char&>

这实际上是投射到char&constvolatile仅存在,因为_Tp可能是constvolatile,而reinterpret_cast可以添加那些,但无法删除

const_cast<char&>

现在constvolatile已被删除。 const_cast可能会这样做。

reinterpret_cast<_Tp*> &(result)

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

答案 3 :(得分:5)

从内到外:

  • 首先它将__r类型转换为const volatile char&:它正在转换为char&,因为它是一种肯定没有重载的类型operator&这做了一些时髦的事情。 const volatile存在,因为这些是限制,可以添加,但不会被reinterpret_cast带走。 _Tp可能已经const和/或volatile,在这种情况下,此演员阵容中需要一个或两个。如果没有,那么演员只是不必要地添加了它们,但它是为最具限制性的演员而写的。

  • 接下来,要取走const volatile您需要一个const_cast,这将导致下一部分...... const_cast<char&>

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