You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

成员函数指针转换逻辑看似合理却出错?标准与编译器表现解析

问题分析:跨无关基类的成员函数指针转换导致崩溃

问题场景

原本认为在C中,将B类的成员函数指针转换为C类成员函数指针后,再转为A类成员函数指针,只要调用目标是C类实体就可以正常运行,但实际运行出现崩溃。经测试:MSVC、GCC编译时报错,Clang编译通过但运行崩溃。请问这属于编译器bug还是未定义行为?C标准对此场景是否有详细说明?

示例代码

#include <iostream>
struct A
{
    char i[7];
    void test1(){
        std::cout << __LINE__;
    }
};

struct B
{
    char i[14];
    virtual void test(){
        std::cout << __LINE__;
    }
};

struct C:A,B
{
    char i[31];
};

using T = void (C::*)();

int main()
{
    C c;
    (c.*T(&B::test))();//正常运行
    (c.*static_cast<decltype(&A::test1)>(T(&B::test)))();//崩溃
    return 0;
} 

解答

这是未定义行为,不是编译器bug

C++标准对成员函数指针的转换有严格限制,你的代码中存在非法的跨无关基类成员指针转换,具体原因如下:

  • 合法的成员指针转换范围
    根据C++标准,static_cast用于成员函数指针转换时,仅允许在同一继承链的类之间进行:

    • 可将派生类的成员函数指针转换为其直接/间接基类的成员函数指针(前提是基类中存在该成员,且转换符合继承规则);
    • 反之,也可将基类的成员函数指针转换为其直接/间接派生类的成员函数指针(如代码中&B::test转T(C::*)这一步是合法的,因为C继承自B)。
  • 非法转换的本质
    代码中第二步,将C::*类型的成员指针转换为A::*类型是非法的:A和B是C的两个独立基类,彼此之间没有继承关系,它们的成员函数指针属于完全无关的类型。这种跨无关类的成员指针转换不在标准允许的范围内,属于未定义行为。

  • 编译器差异的原因

    • MSVC和GCC在编译阶段就检测到这种非法转换,直接抛出错误,这是符合标准的——编译器可以选择诊断这类未定义行为,但不是必须的;
    • Clang允许编译通过,但运行时崩溃是未定义行为的典型表现:编译器不需要强制诊断所有未定义行为,一旦出现,程序的任何行为都是不可预测的(包括崩溃、输出异常结果等)。

C++标准的相关说明

C++标准在[expr.static.cast]章节明确规定:

当static_cast用于转换成员指针时,源类型必须是指向派生类成员的指针,目标类型是指向其基类成员的指针,且基类是派生类的直接或间接基类;或者反过来,源类型是指向基类成员的指针,目标类型是指向其派生类成员的指针,且派生类中该成员的声明与基类兼容。
对于无关类之间的成员指针转换,static_cast是不允许的,此类转换属于未定义行为。

内容的提问来源于stack exchange,提问作者余国良

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:35:03