跨 C++ 模块传递指向成员函数的指针

Passing a pointer to a member function across C++ modules

给定两个 类:XYX 包含类型 Y * 的成员,并将指向其成员函数的指针传递给 Y 的实例。所以在 C++ 中它看起来像这样:

x.h

#pragma once

class Y;

class X
{
public:
    X();
private:
    Y *y;
    void myfunc(int);
};

x.cpp

#include "x.h"
#include "y.h"

X::X()
{
    this->y = new Y(*this, &X::myfunc);
}

void X::myfunc(int dummy)
{
    dummy = dummy;
}

y.h

#pragma once

//#include "x.h"    // this would fix the issue!

class X;

class Y
{
public:
    Y(X &x, void (X::*pMyCallback)(int));
    ~Y();

private:
    X &x;
    void (X::*pMyCallback)(int) = nullptr;
};

y.cpp

#include "y.h"
#include "x.h"

Y::Y(X &x, void (X::*pMyCallback)(int))
    : x(x), pMyCallback(pMyCallback)
{
    (x.*pMyCallback)(3);
}

Y::~Y()
{
}

当我 运行 这段代码时,Visual Studio 2015 引发异常:"MYPROGRAM.exe has triggered a breakpoint"。有时它会因访问冲突异常而崩溃。

int main(int argc, char *argv[])
{
    X x;
    return 0;
}

我注意到这个问题在某种程度上与编译单元有关,因为如果我在同一个文件中定义 XY 它不会崩溃。此外,如果我将 X(即 "x.h")的声明包含在 "y.h" 中,它也不会崩溃。

这种行为有理由吗?

问题似乎是在声明 class 之后定义指向成员的指针时,但在定义之前,MSVC 必须猜测 class' 继承模型。有四种处理方法:

  • 在 "y.h" 中的声明 class X; 中使用 MS 特定关键字 __single_inheritance__multiple_inheritance__virtual_inheritance 来强制它使用特定的模型。其中,__single_inheritance 是最有效的,但仅在 class 没有多重或虚拟继承时可用;因为 X 没有基础 classes,所以这很好。

    // y.h
    
    #pragma once
    
    //#include "x.h"    // this would fix the issue!
    
    class __single_inheritance X;
    
    class Y
    {
    public:
        Y(X &x, void (X::*pMyCallback)(int));
        ~Y();
    
    private:
        X &x;
        void (X::*pMyCallback)(int) = nullptr;
    };
    
  • 出于同样的原因,使用特定于 MS 的 pragma pointers_to_members。同样,我们可以使用 "single inheritance",这是最有效的选择。

    // y.h
    
    #pragma once
    
    #pragma pointers_to_members(full_generality, single_inheritance)
    
    //#include "x.h"    // this would fix the issue!
    
    class X;
    
    class Y
    {
    public:
        Y(X &x, void (X::*pMyCallback)(int));
        ~Y();
    
    private:
        X &x;
        void (X::*pMyCallback)(int) = nullptr;
    };
    
  • 更改IDE中的Pointer-to-member representation选项;不幸的是,我不确定该怎么做,因为我找不到该选项。

  • 指定命令行选项/vmg