使用reinterpret_cast将一个函数转换为void *,为什么不是非法的? [英] Using reinterpret_cast to cast a function to void*, why isn't it illegal?

查看:1045
本文介绍了使用reinterpret_cast将一个函数转换为void *,为什么不是非法的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我之前问题的切线跟进匹配bool的函数的地址vs const void * overload 。回答者解释:


[C ++ 11]标准没有定义从
指向函数的任何标准转换to apointer to void



很难提供 / em>,但是我可以做的
是C ++ 11 4.10 / 2 [conv.ptr]:


指向 cv 类型的prval值 T 其中 T 是对象类型,可以转换为指向 cv
void 类型的prvalue。将指向 cv T 的指针转换为指向 cv void 指向存储位置的开始
,其中类型 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," where T is an object type, can be converted to a prvalue of type "pointer to cv void". The result of converting a "pointer to cv T" to a "pointer to cv void" points to the start of the storage location where the object of type T 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 expression v to type T. If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T 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 using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_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 type T1" can be explicitly converted to a prvalue of a different type "pointer to member of Y of type T2" if T1 and T2 are both function types or both object types.
  • An lvalue expression of type T1 can be cast to the type "reference to T2" if an expression of type "pointer to T1" can be explicitly converted to the type "pointer to T2" 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆