更新:在C ++ 11中,可以使用std::addressof代替boost::addressof。
让我们首先从Boost中复制代码,减去编译器在位周围的工作:
template<class T>
struct addr_impl_ref
{
T & v_;
inline addr_impl_ref( T & v ): v_( v ) {}
inline operator T& () const { return v_; }
private:
addr_impl_ref & operator=(const addr_impl_ref &);
};
template<class T>
struct addressof_impl
{
static inline T * f( T & v, long ) {
return reinterpret_cast<T*>(
&const_cast<char&>(reinterpret_cast<const volatile char &>(v)));
}
static inline T * f( T * v, int ) { return v; }
};
template<class T>
T * addressof( T & v ) {
return addressof_impl<T>::f( addr_impl_ref<T>( v ), 0 );
}
如果我们传递对函数的引用会发生什么?
注意:addressof不能与功能指针一起使用
在C ++中,如果void func();声明了,则func是对不带参数也不返回结果的函数的引用。对该函数的引用可以很容易地转换为指向函数的指针@Konstantin-from:根据13.3.3.2两者T &,T *对于函数而言是无法区分的。第一个是身份转换,第二个是功能到指针转换,均具有“完全匹配”等级(13.3.3.1.1表9)。
对函数的引用经过addr_impl_ref,对于的选择,重载分辨率中存在歧义f,这要归功于dummy参数0,它是第一个参数,int可以提升为long(Integral Conversion)。
因此,我们仅返回指针。
如果我们通过转换运算符传递类型会怎样?
如果转换运算符产生a,T*那么我们就有一个歧义:对于f(T&,long)第二个参数,需要进行整数提升,而对于第一个参数,则需要f(T*,int)对转换运算符进行调用(感谢@litb)
那就是开始的时刻addr_impl_ref。C++标准要求转换序列最多可以包含一个用户定义的转换。通过包装类型addr_impl_ref并强制使用转换序列,我们可以“禁用”该类型附带的任何转换运算符。
因此,f(T&,long)选择了过载(并执行了积分提升)。
其他类型会发生什么?
因此f(T&,long)选择了重载,因为那里的类型与T*参数不匹配。
注意:从文件中有关Borland兼容性的说明来看,数组不会衰减到指针,而是通过引用传递的。
这种过载会发生什么?
我们希望避免应用operator&到该类型,因为它可能已被重载。
该标准保证reinterpret_cast可以用于这项工作(请参阅@Matteo Italia的答案:5.2.10 / 10)。
Boost使用const和volatile限定符添加了一些优点,以避免编译器警告(并正确使用a const_cast删除它们)。
- 投放
T&到char const volatile&
- 去除
const和volatile
- 申请
&接线员取地址
- 投射回
T*
该const/ volatile杂耍有点黑魔法,但它确实简化工作(而不是提供4个重载)。请注意,由于T不合格,如果我们传递ghost const&,则T*是ghost const*,因此限定词并没有真正丢失。
编辑:指针重载用于函数指针,我对上面的解释做了一些修改。我仍然不明白为什么有必要。
以下ideone输出对此进行了某种程度的总结。