7
votes

The header common.h forward declares a class Test and a function receiving a member function pointer:

class Test;

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

In the source file target.cpp, I define the function such that

#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;
}

In my main file, I define the class Test and use the function func:

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

#include "common.h"

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

Obviously this is a bit smelly since pointers to member functions are sometimes more than a simple pointer. But the resulting behavior is a bit overwhelming: The given parameters 0xaaaaaaaa and 0xbbbbbbbb won't be passed correctly to the function. Or to be more precise, the function func interprets the given stack differently than the data is pushed on the stack by the caller. The size of f depends on whether the class is only forward declared or actually defined. The output compiled with Visual Studio 2013 is:

f: 1, x: 0, y: 2130567168

I thought, if a forward declaration is sufficient, it really doesn't matter whether there's a definition given or not.

1
0xaaaaaaaa and 0xbbbbbbbb are not representable as int (under windows calling conventions, see MSDN). The narrowing conversion invokes undefined behaviour. Have you tried using smaller numbers? - Mankarse
I choose these values in order to see them clearly on my stack. Of course, it does not work either if you choose smaller values. And I thought, the narrowing is done during compilation since these values are literals and bound to integer values. - phlipsy
IMHO it's not correct to get a function of forward declared class, because this function could be either virtual or not - borisbn
@borisbn: That's true but neither g++ nor clang++ nor Visual C++ complains about this issue. - phlipsy

1 Answers

6
votes

By default, MSVC favours speed over correctness with pointers to member. You can force it to work according to the standard by passing the compiler flag /vmg.