5

The following source generates warning C4407 in VC and the compiler does indeed produce the incorrect code.

struct A1 {
    int a1;
};

struct A2 {
    int a2;
};

struct B: A1, A2 {
    void f() {
        std::cout << this << '\n';
    }
};

int main() {
    B b = B();
    void (B::*pb)() = &B::f;
    void (A2::*pa)() = (void (A2::*)())pb;  // performs static_cast actually
    std::cout << (std::uintptr_t&)pb << '\n';
    std::cout << (std::uintptr_t&)pa << '\n';
    B* pB = &b;
    A2* pA = pB;
    std::cout << pB << '\n';
    std::cout << pA << '\n';
    (pB->*pb)();
    (pA->*pa)();
}

The code produced is incorrect because pointer pA is not adjusted when invoking pa, leading to a wrong this pointer value in f. However, the code compiles fine in GCC and clang without any warning (except for the strict-aliasing one). Pointer pA is properly adjusted in the code produced by GCC and clang. So, I'm wondering what does the standard say about this? Is the cast in the above code fine according to the standard? Or is it a non-standard extension of GCC and clang?

Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • 4
    MSVC has this feature that allows pointers-to-members to have different size, depending on the complexity of the inheritance hierarchy. This feature is necessarily non-conforming; the standard says that any pointer-to-member can be `reinterpret_cast` to any other and back, resulting in the original value. This pretty much requires that all pointer-to-member types have the same size. You can build with `/vmg` to disable this feature. – Igor Tandetnik Jan 26 '15 at 03:40
  • @IgorTandetnik Your comment is informative, but it does not address my question. My question is about `static_cast` and thunk alike, and the problem exists even if pointer-to-members in MSVC all have the same size. – Lingxi Jan 26 '15 at 04:56
  • The question was: "Does VC conform to the standard with respect to warning C4407?" The answer was: no it doesn't - build with `/vmg` to switch to conforming mode and have the warning go away. [Live example](http://rextester.com/TXGD57689). Which part of your question do you feel remains unaddressed? – Igor Tandetnik Jan 26 '15 at 13:39
  • @IgorTandetnik According to the standard, should the last two statements in `main` print the same pointer value? Should `pA` be offset to address `b` of runtime type `B` before invoking through `pa` which points to `B::f`? – Lingxi Jan 26 '15 at 14:05
  • 1
    Yes, they should print the same pointer value, specifically that of `&b` (which MSVC does, when compiled with `/vmg`). How the compiler achieves this - by "offsetting `pA`" or otherwise - is an irrelevant implementation detail. – Igor Tandetnik Jan 26 '15 at 14:55
  • @IgorTandetnik I get your point. This case-by-case optimization for pointer-to-members is actually a non-standard extension by MSVC :) – Lingxi Jan 26 '15 at 15:44

1 Answers1

2

As per the comments -- this is actually a non-standard extension of MSVC -- GCC and CLang are both handling this correctly by default. Anyone else who sees this should use the /vmg switch on their compiler command line to disable the MSVC extension that allows for 'compacted' PMFs in simple inheritance hierarchies. Unfortunately, the documentation for that switch is quite cryptic -- its relative /vmv is documented in a way that provides more insight as to what's really going on.

LThode
  • 1,843
  • 1
  • 17
  • 28