前置声明类的成员函数指针

7

头文件common.h中声明了一个Test类和一个接收成员函数指针的函数:

class Test;

void func(const Test &t, int (Test::*f)() const, int x, int y);

在源文件target.cpp中,我定义了这个函数如下:
#include "common.h"

void func(const Test &t, int (Test::*f)() const, int x, int y) {
    std::cout << "f: " << (t.*f)() << ", x: " << x << ", y: " << y << std::endl;
}

在我的主文件中,我定义了类Test并使用函数func

class Test {
public:
    int example() const { return 1; }
};

#include "common.h"

int main() {
    Test t;
    func(t, &Test::example, 0xaaaaaaaa, 0xbbbbbbbb);
    return 0;
}

显然,这有点棘手,因为成员函数指针有时不仅仅是一个简单的指针。但是结果行为有点压倒性:给定参数0xaaaaaaaa0xbbbbbbbb将不会正确传递给函数。或者更确切地说,函数func解释给定的堆栈与调用者在堆栈上推送数据不同。 f的大小取决于类是仅前向声明还是实际定义。使用Visual Studio 2013编译的输出如下:
f: 1, x: 0, y: 2130567168

我认为,如果只需要前向声明,是否提供定义其实并不重要。


0xaaaaaaaa0xbbbbbbbb在Windows调用约定下不能表示为int(请参见MSDN)。缩小转换会引发未定义的行为。您尝试使用较小的数字了吗? - Mankarse
我选择这些值是为了在我的堆栈上清晰地看到它们。当然,如果您选择较小的值,也不起作用。我认为,由于这些值是文字常量并绑定到整数值,所以缩小是在编译期间完成的。 - phlipsy
在我看来,获取一个前向声明类的函数是不正确的,因为这个函数可能是虚函数或非虚函数。 - borisbn
@borisbn:这是真的,但是g++、clang++和Visual C++都没有抱怨这个问题。 - phlipsy
1个回答

6

默认情况下,MSVC在处理成员指针时更加偏重于速度而非正确性。你可以通过传递编译器标志/vmg来强制其按照标准工作。


哦,太好了!我在这个问题上浪费了整整一天。 - phlipsy
@phlipsy 我也遇到了同样的问题(也就是说,我也是通过艰难的方式学习到了这个标志)。 - Angew is no longer proud of SO
您是否有关于这个问题的其他信息、链接等? - phlipsy
@phlipsy 只需在答案中提供 MSDN 的链接,但这应该是一个起点。 - Angew is no longer proud of SO

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