C++ 当 operator& 重载时,如何可靠地获取对象的地址?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6494591/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I reliably get an object's address when operator& is overloaded?
提问by James McNellis
Consider the following program:
考虑以下程序:
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 :'(
}
How do I get clyde
's address?
我如何得到clyde
的地址?
I'm looking for a solution that will work equally well for all types of objects. A C++03 solution would be nice, but I'm interested in C++11 solutions too. If possible, let's avoid any implementation-specific behavior.
我正在寻找一种对所有类型的对象都同样适用的解决方案。C++03 解决方案会很好,但我也对 C++11 解决方案感兴趣。如果可能,让我们避免任何特定于实现的行为。
I am aware of C++11's std::addressof
function template, but am not interested in using it here: I'd like to understand how a Standard Library implementor might implement this function template.
我知道 C++11 的std::addressof
函数模板,但我对在这里使用它不感兴趣:我想了解标准库实现者如何实现这个函数模板。
采纳答案by Matthieu M.
Update:in C++11, one may use std::addressof
instead of boost::addressof
.
更新:在 C++11 中,可以使用std::addressof
代替boost::addressof
.
Let us first copy the code from Boost, minus the compiler work around bits:
让我们首先从 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 );
}
What happens if we pass a reference to function?
如果我们传递对 function的引用会发生什么?
Note: addressof
cannot be used with a pointer to function
注意:addressof
不能与指向函数的指针一起使用
In C++ if void func();
is declared, then func
is a reference to a function taking no argument and returning no result. This reference to a function can be trivially converted into a pointer to function -- from @Konstantin
: According to 13.3.3.2 both T &
and T *
are indistinguishable for functions. The 1st one is an Identity conversion and the 2nd one is Function-to-Pointer conversion both having "Exact Match" rank (13.3.3.1.1 table 9).
在 C++ 中,如果void func();
声明了,则func
是对不带参数且不返回结果的函数的引用。这种对函数的引用可以简单地转换为指向函数的指针——来自@Konstantin
:根据 13.3.3.2T &
和T *
函数无法区分。第一个是身份转换,第二个是函数到指针的转换,两者都具有“完全匹配”等级(13.3.3.1.1 表 9)。
The reference to functionpass through addr_impl_ref
, there is an ambiguity in the overload resolution for the choice of f
, which is solved thanks to the dummy argument 0
, which is an int
first and could be promoted to a long
(Integral Conversion).
对函数pass through的引用addr_impl_ref
,在重载决议中对于 的选择存在歧义f
,这要归功于虚拟参数0
,这是int
第一个并且可以提升为long
(积分转换)。
Thus we simply returns the pointer.
因此我们简单地返回指针。
What happens if we pass a type with a conversion operator ?
如果我们传递带有转换运算符的类型会发生什么?
If the conversion operator yields a T*
then we have an ambiguity: for f(T&,long)
an Integral Promotion is required for the second argument while for f(T*,int)
the conversion operator is called on the first (thanks to @litb)
如果转换运算符产生 a ,T*
那么我们有一个歧义:对于f(T&,long)
第二个参数需要积分提升,而在第一个参数f(T*,int)
上调用转换运算符(感谢@litb)
That's when addr_impl_ref
kicks in. The C++ Standard mandates that a conversion sequence may contain at most one user-defined conversion. By wrapping the type in addr_impl_ref
and forcing the use of a conversion sequence already, we "disable" any conversion operator that the type comes with.
这就是开始的时候addr_impl_ref
。C++ 标准要求转换序列最多可以包含一个用户定义的转换。通过包装类型addr_impl_ref
并强制使用转换序列,我们“禁用”了该类型附带的任何转换运算符。
Thus the f(T&,long)
overload is selected (and the Integral Promotion performed).
因此f(T&,long)
选择了过载(并执行了积分提升)。
What happens for any other type ?
任何其他类型会发生什么?
Thus the f(T&,long)
overload is selected, because there the type does not match the T*
parameter.
因此f(T&,long)
选择了重载,因为类型与T*
参数不匹配。
Note: from the remarks in the file regarding Borland compatibility, arrays do not decay to pointers, but are passed by reference.
注意:根据文件中关于 Borland 兼容性的说明,数组不会衰减为指针,而是通过引用传递。
What happens in this overload ?
在这种超载中会发生什么?
We want to avoid applying operator&
to the type, as it may have been overloaded.
我们希望避免应用operator&
到该类型,因为它可能已被重载。
The Standard guarantees that reinterpret_cast
may be used for this work (see @Matteo Italia's answer: 5.2.10/10).
标准保证reinterpret_cast
可用于这项工作(参见@Matteo Italia 的回答:5.2.10/10)。
Boost adds some niceties with const
and volatile
qualifiers to avoid compiler warnings (and properly use a const_cast
to remove them).
Boost 添加了一些细节 withconst
和volatile
限定符以避免编译器警告(并正确使用 aconst_cast
来删除它们)。
- Cast
T&
tochar const volatile&
- Strip the
const
andvolatile
- Apply the
&
operator to take the address - Cast back to a
T*
- 投射
T&
到char const volatile&
- 剥离
const
和volatile
- 应用
&
运算符取地址 - 投射回一个
T*
The const
/volatile
juggling is a bit of black magic, but it does simplify the work (rather than providing 4 overloads). Note that since T
is unqualified, if we pass a ghost const&
, then T*
is ghost const*
, thus the qualifiers have not really been lost.
该const
/volatile
杂耍有点黑魔法,但它确实简化工作(而不是提供4个重载)。请注意,由于T
是不合格的,如果我们通过 a ghost const&
,则T*
是ghost const*
,因此限定符并没有真正丢失。
EDIT:the pointer overload is used for pointer to functions, I amended the above explanation somewhat. I still do not understand why it is necessarythough.
编辑:指针重载用于指向函数的指针,我稍微修改了上面的解释。我仍然不明白为什么有必要。
The following ideone outputsums this up, somewhat.
以下ideone 输出在某种程度上总结了这一点。
回答by Konrad Rudolph
Use std::addressof
.
You can think of it as doing the following behind the scenes:
您可以将其视为在幕后执行以下操作:
- Reinterpret the object as a reference-to-char
- Take the address of that (won't call the overload)
- Cast the pointer back to a pointer of your type.
- 将对象重新解释为对字符的引用
- 取那个地址(不会调用重载)
- 将指针转换回您类型的指针。
Existing implementations (including Boost.Addressof) do exactly that, just taking additional care of const
and volatile
qualification.
现有的实现(包括 Boost.Addressof)正是这样做的,只是额外注意const
和volatile
限定。
回答by Matteo Italia
The trick behind boost::addressof
and the implementation provided by @Luc Danton relies on the magic of the reinterpret_cast
; the standard explicitly states at §5.2.10 ?10 that
背后的技巧boost::addressof
和@Luc Danton 提供的实现依赖于reinterpret_cast
; 该标准在 §5.2.10 ?10 中明确指出
An lvalue expression of type
T1
can be cast to the type “reference toT2
” if an expression of type “pointer toT1
” can be explicitly converted to the type “pointer toT2
” using areinterpret_cast
. That is, a reference castreinterpret_cast<T&>(x)
has the same effect as the conversion*reinterpret_cast<T*>(&x)
with the built-in&
and*
operators. The result is an lvalue that refers to the same object as the source lvalue, but with a different type.
类型的左值表达式
T1
可以转换为类型“参照T2
”如果类型的“指针的表达T1
”可以显式转换到类型“指针T2
”使用reinterpret_cast
。也就是说,引用reinterpret_cast<T&>(x)
转换*reinterpret_cast<T*>(&x)
与使用内置&
和*
运算符的转换具有相同的效果。结果是一个左值,它引用与源左值相同的对象,但具有不同的类型。
Now, this allows us to convert an arbitrary object reference to a char &
(with a cv qualification if the reference is cv-qualified), because any pointer can be converted to a (possibly cv-qualified) char *
. Now that we have a char &
, the operator overloading on the object is no longer relevant, and we can obtain the address with the builtin &
operator.
现在,这允许我们将任意对象引用转换为 a char &
(如果引用是 cv 限定的,则使用 cv 限定),因为任何指针都可以转换为 a(可能是 cv 限定的)char *
。现在我们有了 a char &
,对象上的运算符重载不再相关,我们可以使用内置&
运算符获取地址。
The boost implementation adds a few steps to work with cv-qualified objects: the first reinterpret_cast
is done to const volatile char &
, otherwise a plain char &
cast wouldn't work for const
and/or volatile
references (reinterpret_cast
cannot remove const
). Then the const
and volatile
is removed with const_cast
, the address is taken with &
, and a final reinterpet_cast
to the "correct" type is done.
boost 实现添加了几个步骤来处理 cv 限定的对象:第一个reinterpret_cast
是对 完成的const volatile char &
,否则普通转换char &
将不适用于const
和/或volatile
引用(reinterpret_cast
无法删除const
)。然后const
和volatile
与删除const_cast
,地址被采取&
,并最终reinterpet_cast
以“正确”的类型完成。
The const_cast
is needed to remove the const
/volatile
that could have been added to non-const/volatile references, but it does not "harm" what was a const
/volatile
reference in first place, because the final reinterpret_cast
will re-add the cv-qualification if it was there in first place (reinterpret_cast
cannot remove the const
but can add it).
将const_cast
需要删除const
/volatile
可能已被添加到非const /易失性的参考,但它做什么,是不是“伤害” const
/volatile
放在第一位参考,因为最终reinterpret_cast
会重新添加CV-资格,如果它是首先是(reinterpret_cast
不能删除const
但可以添加它)。
As for the rest of the code in addressof.hpp
, it seems that most of it is for workarounds. The static inline T * f( T * v, int )
seems to be needed only for the Borland compiler, but its presence introduces the need for addr_impl_ref
, otherwise pointer types would be caught by this second overload.
至于 中的其余代码addressof.hpp
,似乎大部分都是为了解决方法。在static inline T * f( T * v, int )
似乎只需要在Borland编译,但它的出现引入了必要addr_impl_ref
的,否则指针类型将通过本次超载被抓。
Edit: the various overloads have a different function, see @Matthieu M. excellent answer.
编辑:各种重载具有不同的功能,请参阅@Matthieu M. 优秀答案。
Well, I'm no longer sure of this either; I should further investigate that code, but now I'm cooking dinner :) , I'll have a look at it later.
好吧,我也不再确定了。我应该进一步调查该代码,但现在我正在做饭 :) ,我稍后再看看。
回答by Luc Danton
I've seen an implementation of addressof
do this:
我见过addressof
这样做的实现:
char* start = &reinterpret_cast<char&>(clyde);
ghost* pointer_to_clyde = reinterpret_cast<ghost*>(start);
Don't ask me how conforming this is!
不要问我这有多合规!
回答by Konstantin Oznobihin
Take a look at boost::addressofand its implementation.
看看boost::addressof及其实现。