更新:在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输出对此进行了某种程度的总结。