使用reinterpret_cast将一个函数转换为void *,为什么不是非法的? [英] Using reinterpret_cast to cast a function to void*, why isn't it illegal?
问题描述
这是我之前问题的切线跟进匹配bool的函数的地址vs const void * overload 。回答者解释:
[C ++ 11]标准没有定义从
指向函数的任何标准转换to apointer tovoid
。
很难提供 / em>,但是我可以做的
是C ++ 11 4.10 / 2 [conv.ptr]:
指向 cv 类型的prval值
T
,其中T
是对象类型,可以转换为指向 cv
void
类型的prvalue。将指向 cvT
的指针转换为指向 cvvoid
指向存储位置的开始
,其中类型T
的对象驻留,是类型T的最多
派生对象(1.8)(也就是说,不是基类子对象)。
空指针值将转换为
目标类型的空指针值。
)
假设 func
被声明为 void func );
,如果你做一个C风格的转型,即(void *)func
,转换将成功。 static_cast< void *>(func)
但无效,但 reinterpret_cast< void *>(func)
成功的。然而,你不能做的是将随后转换的指针转换回其原始类型。例如,
好:
int main(){
int * i;
void * s = static_cast< void *>(i);
i = static_cast< int *>(s)
s = reinterpret_cast< void *>(i);
i = reinterpret_cast< int *>(s);
}
不好:
void func(){}
int main(){
void * s = reinterpret_cast< void *>(func);
reinterpret_cast< decltype(func)>(s);
}
N3337 开头是
[expr.reinterpret.cast]
表达式
reinterpret_cast< T>(v)
的结果是结果
将表达式v
转换为T
。如果T
是一个左值
引用类型或对函数类型的右值引用,结果是
a lvalue;如果T
是对象类型的右值引用,结果是
和xvalue;否则,结果是一个prvalue,并且左值到右值
(4.1),数组到指针(4.2)和函数到指针(4.3)标准
转换在表达式v。可以使用reinterpret_cast
显式执行的
转换如下所示。否
其他转换可以使用reinterpret_cast
显式执行。
$ b b
我粗鲁的语言,我相信是关键在这里。最后一部分似乎暗示,如果转换未列出,这是非法的。总而言之,允许的转换是:
- 指针可以显式转换为任何大小以容纳它的整数类型。
- 整数类型或枚举类型的值可以显式转换为指针。
- 函数指针可以显式转换为不同类型的函数指针。
- 对象指针可以显式转换为不同类型的对象指针。
- 将函数指针转换为对象指针类型,反之亦然是有条件支持的。
- 空指针值(4.10)将转换为目标类型的空指针值。
-
T1类型的
如果X
成员的类型的prvalue类型指针T1
,则可以显式转换为不同类型的prvalue指向T2
T 2都是函数类型或两个对象类型。
- 类型
T1的左值表达式
可以转换为类型引用
T2
,如果指向T1
可以显式转换为指向T2
的类型指针。
void *
不是函数指针,或void类型。
[basic.types]
(可能是cv限定的)类型,不是
函数类型,而不是引用类型。
所以也许我正在抓住吸管,但看起来 reinterpret_cast< void *>(func)
是非法的。然而,另一方面,[expr.static.cast] / 5说否则, static_cast
将执行下面列出的转换之一,没有其他转换将
使用 static_cast
显式地执行。关键区别是将和可以。这是否足以使 reinterpret_cast
合法或者我缺少其他的东西?
(所有引用都来自N3337,并且对于每一个草稿都是等效的,直到N4296,即此答案至少对于C ++ 11和C ++ 14 有效,但不适用于C + +03 ,因为此回答的第一个报价不存在。)
[expr.reinterpret.cast] / 8: / p>
将函数指针转换为对象指针类型或反之亦然
是有条件支持的。这种转换的意义是
实现定义的,除了如果一个实现支持两个方向的
转换,将一个类型的prvalue转换为
另一个类型并返回,可能具有不同的cv -qualification,
将产生指针值。
这包含在您的列表中。你认为 void
不是一个对象类型,但你没有考虑到关键的[basic.compound] / 3:
指向
。void
的指针或指向对象类型的指针称为
这是
$的唯一原因。 b $ b
f = reinterpret_cast< decltype(f)>(s)
在GCC或Clang是不是罚款是目标类型,是不是衰减 - 你可以清楚地不将 void *
转换为函数类型。您需要将目标类型作为函数的指针, 然后它工作 。 / p>
This is a tangential follow up to my previous question The address of a function matching a bool vs const void* overload. The answerer explained:
The [C++11] standard does not define any standard conversions from a "pointer to function" to a "pointer to
void
."It's hard to provide a quote for the absence of something, but the closest I can do is C++11 4.10/2 [conv.ptr]:
A prvalue of type "pointer to cv
T
," whereT
is an object type, can be converted to a prvalue of type "pointer to cvvoid
". The result of converting a "pointer to cvT
" to a "pointer to cvvoid
" points to the start of the storage location where the object of typeT
resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject). The null pointer value is converted to the null pointer value of the destination type.(emphasis mine)
Assuming func
is declared void func();
, if you do a C-style cast, i.e. (void*) func
, the cast will be successful. static_cast<void*>(func)
however is invalid, but reinterpret_cast<void*>(func)
will be successful. What you cannot do however is convert the subsequently converted pointer back to its original type. For example,
Fine:
int main() {
int* i;
void* s = static_cast<void*>(i);
i = static_cast<int*>(s);
s = reinterpret_cast<void*>(i);
i = reinterpret_cast<int*>(s);
}
Not fine:
void func() { }
int main() {
void* s = reinterpret_cast<void*>(func);
reinterpret_cast<decltype(func)>(s);
}
N3337 starts off by saying,
[expr.reinterpret.cast]
The result of the expression
reinterpret_cast<T>(v)
is the result of converting the expressionv
to typeT
. IfT
is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; ifT
is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the expression v. Conversions that can be performed explicitly usingreinterpret_cast
are listed below. No other conversion can be performed explicitly usingreinterpret_cast
.
I bolded the language that I believe is key here. The last part seems to imply that if the conversion is not listed, it's illegal. In brief summary, the allowed conversions are:
- A pointer can be explicitly converted to any integral type large enough to hold it.
- A value of integral type or enumeration type can be explicitly converted to a pointer.
- A function pointer can be explicitly converted to a function pointer of a different type.
- An object pointer can be explicitly converted to an object pointer of a different type.
- Converting a function pointer to an object pointer type or vice versa is conditionally-supported.
- The null pointer value (4.10) is converted to the null pointer value of the destination type.
- A prvalue of type "pointer to member of
X
of typeT1
" can be explicitly converted to a prvalue of a different type "pointer to member of Y of typeT2
" ifT1
andT
2 are both function types or both object types. - 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 a reinterpret_cast.
void*
is not a function pointer and objects don't have function or void type.
[basic.types]
An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not a void type.
So maybe I'm grasping at straws, but it seems reinterpret_cast<void*>(func)
is illegal. However, on the other hand, [expr.static.cast]/5 says "Otherwise, the static_cast
shall perform one of the conversions listed below. No other conversion shall be
performed explicitly using a static_cast
." the key difference being "shall" and "can". Is this enough to make the reinterpret_cast
legal or am I missing something else?
(All quotes are from N3337 and are equivalent for every single draft until N4296 from there on, i.e. this answer is valid at least for C++11 and C++14 but not for C++03 as the first quote of this answer does not exist in there.)
[expr.reinterpret.cast]/8:
Converting a function pointer to an object pointer type or vice versa is conditionally-supported. The meaning of such a conversion is implementation-defined, except that if an implementation supports conversions in both directions, converting a prvalue of one type to the other type and back, possibly with different cv-qualification, shall yield the orignal pointer value.
This is contained in your listing. You argue that void
is not an object type, but you didn't consider the crucial [basic.compound]/3:
The type of a pointer to
void
or a pointer to an object type is called an object pointer type.
(That is, a object pointer type is not necessarily a "pointer to object type" - standard terminology got you there.)
The only reason that
f = reinterpret_cast<decltype(f)>(s);
Isn't fine on GCC or Clang is that the target type, as opposed to the source expression, is not decayed - and you can clearly not cast void*
to a function type. You need to make the target type a pointer to function, then it works.
这篇关于使用reinterpret_cast将一个函数转换为void *,为什么不是非法的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!