Jam*_*lis 167 c++ operator-overloading memory-address c++11
考虑以下程序:
struct ghost
{
// ghosts like to pretend that they don't exist
ghost* operator&() const volatile { return 0; }
};
int main()
{
ghost clyde;
ghost* clydes_address = &clyde; // darn; that's not clyde's address :'(
}
Run Code Online (Sandbox Code Playgroud)
我怎么得到clyde地址?
我正在寻找一种适用于所有类型对象的解决方案.C++ 03解决方案会很好,但我也对C++ 11解决方案感兴趣.如果可能,让我们避免任何特定于实现的行为.
我知道C++ 11的std::addressof函数模板,但我不想在这里使用它:我想了解标准库实现者如何实现这个函数模板.
Mat*_* M. 98
更新:在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 );
}
Run Code Online (Sandbox Code Playgroud)
如果我们传递对函数的引用会发生什么?
注意:addressof不能与指向函数的指针一起使用
在C++ void func();中声明,然后func是对不带参数且不返回结果的函数的引用.这个对函数的引用可以简单地转换为指向函数的指针 - 来自@Konstantin:根据13.3.3.2两者T &并且T *对于函数是无法区分的.第一个是身份转换,第二个是具有"精确匹配"等级的功能到指针转换(13.3.3.1.1表9).
对函数的引用通过addr_impl_ref,选择的重载决策存在歧义f,这可以通过伪参数来解决,伪参数0是int第一个,可以提升为long(积分转换).
因此我们只返回指针.
如果我们传递带转换运算符的类型会发生什么?
如果转换运算符产生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输出在某种程度上总结了这一点.
Kon*_*lph 97
本质上,您可以将对象重新解释为char-reference-reference,获取其地址(不会调用重载)并将指针强制转换为您的类型的指针.
Boost.AddressOf代码就是这样做的,只需要额外注意volatile和const鉴定.
Mat*_*lia 49
boost::addressof@Luc Danton提供的诀窍和实施依赖于魔术reinterpret_cast; 该标准明确规定§5.2.1010
如果类型"指向" 的表达式可以使用a 显式转换为"指向" 的类型,则
T1可以将类型的左值表达式强制转换为"引用T2"类型.也就是说,参考演员与使用内置和运算符的转换具有相同的效果.结果是一个左值,它引用与源左值相同的对象,但具有不同的类型.T1T2reinterpret_castreinterpret_cast<T&>(x)*reinterpret_cast<T*>(&x)&*
现在,这允许我们将任意对象引用转换为char &(如果引用是cv限定的,则具有cv限定条件),因为任何指针都可以转换为(可能是cv限定的)char *.现在我们有一个char &,对象上的运算符重载不再相关,我们可以使用内置&运算符获取地址.
boost实现添加了几个步骤来处理cv限定的对象:第一个reinterpret_cast是完成的const volatile char &,否则一个普通的char &强制转换不适用于const和/或volatile引用(reinterpret_cast不能删除const).然后const和volatile与删除const_cast,地址被采取&,并最终reinterpet_cast以"正确"的类型完成.
将const_cast需要删除const/ volatile可能已被添加到非const /易失性的参考,但它没有"伤害"什么是const/ volatile排在首位的参考,因为最终reinterpret_cast会重新添加CV-资格,如果它是在第一位(reinterpret_cast不能删除const但可以添加它).
至于代码的其余部分addressof.hpp,似乎大多数代码都是为了解决方法.在static inline T * f( T * v, int )似乎只需要在Borland编译,但它的出现引入了必要addr_impl_ref的,否则指针类型将通过本次超载被抓.
编辑:各种重载都有不同的功能,请参阅 @Matthieu M.优秀答案.
好吧,我也不再确定这一点; 我应该进一步调查这段代码,但现在我正在做晚餐:),我稍后会看一下.
Luc*_*ton 11
我已经看到了addressof这样做的实现:
char* start = &reinterpret_cast<char&>(clyde);
ghost* pointer_to_clyde = reinterpret_cast<ghost*>(start);
Run Code Online (Sandbox Code Playgroud)
不要问我这是多么的顺从!
| 归档时间: |
|
| 查看次数: |
21006 次 |
| 最近记录: |