13

I don't understand why adding a forward declaration for a class changes a size of its pointer to member type

#include <iostream>
using namespace std;

int main()
{
    //struct CL;
    //cout<<sizeof(int (CL::*)())<<endl; 
    struct CL{};
    cout<<sizeof(int (CL::*)())<<endl;
}

output VS2013:
4

But if I uncomment the first two lines in main(), then the output is different:
16
16

So, only a simple adding a forward declaration before a definition of struct CL increases a size of a pointer to member of CL. Why? I know that a size of member function pointer depends by structure of a type (e.g. virtual functions and base classes may increase it), but why can the sizeof operator be applied to a pointer to member of an incomplete type? Or it can't? I have not found it in the Standard

phantom
  • 3,292
  • 13
  • 21
Denis
  • 2,786
  • 1
  • 14
  • 29
  • `struct CL{}` is definitely a local class in the function. Not sure about `struct CL;` Anyways, it looks like a bug of msvc –  Jun 18 '15 at 18:54
  • 1
    @DieterLücking `struct CL;` is a forward-declaration of a local class in this case, see [basic.scope.pdecl]p7.1 – dyp Jun 18 '15 at 18:58
  • See also: https://social.msdn.microsoft.com/Forums/vstudio/en-US/a9cfa5c4-d90b-4c33-89b1-9366e5fbae74/strange-error-while-casting-pointers-to-member-functions?forum=vclanguage (via http://stackoverflow.com/a/13881429/ ) – dyp Jun 18 '15 at 19:01
  • @dyp Is that another reason to hate msvc? –  Jun 18 '15 at 19:05
  • @DieterLücking It is another reason to set it up properly ;) Just like `-Wall -Wextra -pedantic -std=c++??` in g++, you need to deactivate language extensions in cl, possibly deactivate comdat folding, and force it to use the same size for all member pointers. – dyp Jun 18 '15 at 19:08

1 Answers1

11

The MSVC compiler uses different sizes for pointers to member functions as an optimization. This optimization violates the Standard. Kudos to Igor Tandetnik mentioning reinterpret_cast in a MSDN form post, [expr.reinterpret.cast]p10

A prvalue of type “pointer to member of X of type T1” can be explicitly converted to a prvalue of a different type “pointer to member of Y of type T2” if T1 and T2 are both function types or both object types. The null member pointer value is converted to the null member pointer value of the destination type. The result of this conversion is unspecified, except in the following cases:

  • converting a prvalue of type “pointer to member function” to a different pointer to member function type and back to its original type yields the original pointer to member value.

So there's a roundtrip guarantee, this effectively forces conforming implementations to use the same size for all pointer to member function types.


The MSVC optimization is performed if the /vmb switch is set. For the case of single inheritance, the optimised pointer to member function requires only a void*-sized storage, see The Old New Thing: Pointers to member functions are very strange animals.

If you only forward-declare the type CL and then form a pointer-to-member function, the optimization hopefully is deactivated (I could not find any documentation on that, unfortunately). Otherwise, you might get inconsistent sizes before and after the definition of CL.

By the way, you can get inconsistent sizes for enumerations in VS2010, if you forward-declare them without specifying an underlying type and later explicitly define the underlying type for the definition of the enum. This works only with language extensions activated.

Chronial
  • 66,706
  • 14
  • 93
  • 99
dyp
  • 38,334
  • 13
  • 112
  • 177
  • There is also a [pragma](https://msdn.microsoft.com/en-us/library/83cch5a6.aspx) which claims that you need to define the class before you can form a pointer-to-member to it. But that's not true at least in VS2013.. – dyp Jun 18 '15 at 19:40