之前一直没有意识到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*>
很明显。剩下的就是黑魔法。有人可以分解这实际上是如何运作的吗?
答案 0 :(得分:30)
__r
类型为_Tp&
reinterpret_cast
为char&
,以确保能够稍后获取其地址而不必担心原始类型中的operator&
过载;实际上它被转换为const volatile char&
因为reinterpret_cast
总是可以合法地添加const
和volatile
限定符,即使它们不存在,但如果它们存在则无法删除它们(这确保了_Tp
最初的限定符,它们不会干扰演员表。)const_cast
仅限于char&
,删除限定符(现在合法!const_cast
可以执行reinterpret_cast
无法对限定符执行的操作)。 &
(现在我们有一个普通的char*
)reinterpret_cast
已追溯至_Tp*
(其中包括原始const
和volatile
限定符(如果有)。 编辑,因为我的答案已经被接受,我会彻底并补充说char
作为中间类型的选择是由于对齐问题导致的,以避免触发Undefined行为。有关完整说明,请参阅@ JamesKanze的评论(在问题下)。感谢詹姆斯如此清楚地解释它。
答案 1 :(得分:12)
当你想到它时,它实际上非常简单,为了在一个过载的operator&
的前提下获得一个对象/函数的真实地址,你需要将该对象视为一个非实际的东西,一些不能有重载运算符的类型.. 内在类型(例如char
)。
char
没有对齐,并且可以驻留在任何其他对象可以的任何位置,具有该说法;将对象转换为对char的引用是一个非常好的开始。
但是在执行reinterpret_cast<const volatile char&>
时会涉及黑魔法
为了重新解释来自addressof
实现的返回指针,我们最终会想要放弃const
和volatile
等限定符(最终结果)用简单的引用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_cast
和reinterpret_cast
的限制,转化是在两次演员表中完成的。
版本较长:
它正在进行三次连续演员表演。
reinterpret_cast<const volatile char&>
这实际上是投射到char&
。 const
和volatile
仅存在,因为_Tp
可能是const
或volatile
,而reinterpret_cast
可以添加那些,但无法删除。
const_cast<char&>
现在const
和volatile
已被删除。 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
,这意味着此时可以添加这些内容。