为什么当将函数 static_cast(ing) 为 void* 时,编译器的行为会有所不同?

9
以下代码在VSC++2017中编译时没有任何错误,在gcc 7.3.0中无法编译 (error: invalid static_cast from type 'int(int)' to type 'void*' void* p = static_cast<void*>(func))。
#include <iostream>

int func(int x) { return 2 * x; }

int main() {

    void* p = static_cast<void*>(func);
    return 0;
}

1
函数指针有点奇怪。我得去深入了解标准,但我很确定MSVC正在为他们自己的邪恶目的扭曲标准。 - user4581301
2
@user4581301 不完全是这样 - 另一个问题是关于C语言的,而且这些语言可能存在差异... - Aconcagua
1
虽然函数指针和对象指针是不同的类型,但它们之间的大多数不兼容性问题都发生在它们的 sizeof() 大小不同时。如果它们的大小相同,通常可以安全地相互转换为 void*。即便如此,虽然这种方法可能有效,但它并不具备可移植性,最好还是避免使用。 - doug
1个回答

8

函数只能隐式转换为函数指针。在语言中,函数指针不是严格意义上的指针,后者仅指向对象。

不能使用static_cast将函数指针转换为void*。所示程序格式错误。如果编译器没有警告,则它未符合标准。无法编译格式错误的程序不违反标准。


在保证void*能够指向函数的系统(如POSIX)中,可以使用reinterpret_cast代替:

void* p = reinterpret_cast<void*>(func);

但是这种方法无法在没有该保证的系统上移植。(我不知道是否存在有C++编译器但没有此保证的系统,但这并不意味着不存在这样的系统)。
标准引用:
[expr.reinterpret.cast] 将函数指针转换为对象指针类型或反之是有条件支持的。这种转换的含义是实现定义的,除非实现支持双向转换,将一个类型的prvalue转换为另一个类型,然后再转回来,可能具有不同的cv限定符,应产生原始指针值。
请注意,这种有条件的支持不适用于成员函数指针。 成员函数指针不是函数指针。

2
POSIX 要求函数指针(代码地址)和对象指针(数据地址)之间必须具有互相转换的能力,这是使 dlsym 生效所必需的。 - Ben Voigt
1
@Peter 标准要求如果程序格式不正确(除非另有规定),则需要进行诊断。所示程序格式不正确,需要进行诊断。警告是诊断的一种形式,发出警告就足以符合标准。缺乏诊断违反了标准。 - eerorika
2
@eerorika - 你混淆了术语。在标准中,“诊断”一词具有特定的定义,只有在指定情况下才需要符合标准。警告通常提供额外信息,这不是标准要求的,因为供应商选择这样做,作为开发人员的辅助(通常是因为编译器进行了更多分析,因此可以提供额外信息)。对于你来说,警告可能是诊断。根据C++标准的定义,它不是。 - Peter
2
@Peter:警告是一种诊断。defns.diagnostic:“属于实现定义的子集的实现输出消息”。 - geza
2
@SoulimaneMammar:标准已经处理了这个问题。它说转换是否被支持是实现定义的。这是标准能够对此发表的最好意见(因为可能存在/曾经存在无法进行转换的平台)。但是目前广泛使用的所有平台上都很有可能支持转换(如果平台具有dlsym/GetProcAddress,则应该可以工作)。 - geza
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接