VC++ and Empty Base Class Optimization problem/bug
-
Here is the test code: http://codepad.org/zKTatneG[^] struct Empty { }; struct JustLong { long data; }; struct LongAndEmpty : public JustLong, private Empty { }; struct StringAndEmpty: public std::string, private Empty { }; int main() { cout << "Empty:" << sizeof(Empty) << endl; cout << "JustLong:" << sizeof(JustLong) << endl; cout << "String:" << sizeof(std::string) << endl; cout << "LongAndEmpty:" << sizeof(LongAndEmpty) << endl; cout << "StringAndEmpty:" << sizeof(StringAndEmpty) << endl; return 0; } When you try it in other compiler (for example, see link above for codepad.org), results are correct, i.e. output looks like this: Empty:1 JustLong:4 String:4 LongAndEmpty:4 StringAndEmpty:4 Now, when you try this in VC++ (2010), size of struct with std::string is always (debug and release) 4 more that size of just std::string. Any other structs/classes are of correct size, and only those with std::string are bigger than they should be. Am I missing something or this is MS bug?
-
Here is the test code: http://codepad.org/zKTatneG[^] struct Empty { }; struct JustLong { long data; }; struct LongAndEmpty : public JustLong, private Empty { }; struct StringAndEmpty: public std::string, private Empty { }; int main() { cout << "Empty:" << sizeof(Empty) << endl; cout << "JustLong:" << sizeof(JustLong) << endl; cout << "String:" << sizeof(std::string) << endl; cout << "LongAndEmpty:" << sizeof(LongAndEmpty) << endl; cout << "StringAndEmpty:" << sizeof(StringAndEmpty) << endl; return 0; } When you try it in other compiler (for example, see link above for codepad.org), results are correct, i.e. output looks like this: Empty:1 JustLong:4 String:4 LongAndEmpty:4 StringAndEmpty:4 Now, when you try this in VC++ (2010), size of struct with std::string is always (debug and release) 4 more that size of just std::string. Any other structs/classes are of correct size, and only those with std::string are bigger than they should be. Am I missing something or this is MS bug?
First it's not technically a bug unless it either doesn't work or doesn't meet the C++ spec. I don't think either of these things is the case. Second it looks much more likely that
StringAndEmpty
is larger because it's derived from an already complex class the v-table for which is compiled into an external module. It's a guess but I reckon you've got an extra pointer in there to look up the pre-existing std::string vtable. I have read a full explanation of the various sizes and formats of MSVC and other compilers minimal/empty/simple/complex structs and classes with and without virtual inheritance. I can neither remember all the details nor where I read it but I do remember that there were a lot more variations 4-byte, 8-byte, 12-byte, 16-byte, 20-byte 'headers' than I would ever have thought and much of the variation was within one type of compiler rather than between them. If I remember where that detailed research is I'll post a link."The secret of happiness is freedom, and the secret of freedom, courage." Thucydides (B.C. 460-400)
-
First it's not technically a bug unless it either doesn't work or doesn't meet the C++ spec. I don't think either of these things is the case. Second it looks much more likely that
StringAndEmpty
is larger because it's derived from an already complex class the v-table for which is compiled into an external module. It's a guess but I reckon you've got an extra pointer in there to look up the pre-existing std::string vtable. I have read a full explanation of the various sizes and formats of MSVC and other compilers minimal/empty/simple/complex structs and classes with and without virtual inheritance. I can neither remember all the details nor where I read it but I do remember that there were a lot more variations 4-byte, 8-byte, 12-byte, 16-byte, 20-byte 'headers' than I would ever have thought and much of the variation was within one type of compiler rather than between them. If I remember where that detailed research is I'll post a link."The secret of happiness is freedom, and the secret of freedom, courage." Thucydides (B.C. 460-400)
Well, consider the following struct: struct JustString: public std::string { }; It has the same size as just std::string, so no, there is no extra pointer in VFT. The question I have is about Empty Base Class Optimization - size of empty base class should be reduced to zero in descendant if possible. Yes, it is not a requirement, only a suggestion in a standard. Problem is, I don't understand this: it works for non-MS compilers; in MS-compiler it works for all cases EXCEPT std:string. I will check later with VC++ 2012 to see if they made it work, but right now I have no other explanation than some problems with MS compiler. Or is there any compiler option specifically for this case that I don't know about?
-
Well, consider the following struct: struct JustString: public std::string { }; It has the same size as just std::string, so no, there is no extra pointer in VFT. The question I have is about Empty Base Class Optimization - size of empty base class should be reduced to zero in descendant if possible. Yes, it is not a requirement, only a suggestion in a standard. Problem is, I don't understand this: it works for non-MS compilers; in MS-compiler it works for all cases EXCEPT std:string. I will check later with VC++ 2012 to see if they made it work, but right now I have no other explanation than some problems with MS compiler. Or is there any compiler option specifically for this case that I don't know about?
The example you give would not need an 'extra' pointer because it would only need the one to the external vtable as everything internal can be optimised away. So far only when you have an internal and an external base you have found the size increases. It's likely that even in that case the internal base could be optimised away but it isn't which may be a flaw in the Microsoft compiler or there may be some practical reason why that shouldn't be done. As I mentioned there are quite a number of combinations of single vs multiple inheritance, virtual and non-virtual bases, local and external bases, exported and non exported, complete and incomplete classes at the point of first declaration. Some optimisations such as empty base class removal will be available under some combinations and not others. For a comparison you could try the Clang compiler which is more C++11 compliant than MSVC and being open source has support forums where you can ask exactly why they did it a particular way. Some odd behaviour in this area in MSVC is more about backward compatability of the resulting binaries with older version of the compiler and of Windows than about mistakes in the current version.
"The secret of happiness is freedom, and the secret of freedom, courage." Thucydides (B.C. 460-400)
-
Well, consider the following struct: struct JustString: public std::string { }; It has the same size as just std::string, so no, there is no extra pointer in VFT. The question I have is about Empty Base Class Optimization - size of empty base class should be reduced to zero in descendant if possible. Yes, it is not a requirement, only a suggestion in a standard. Problem is, I don't understand this: it works for non-MS compilers; in MS-compiler it works for all cases EXCEPT std:string. I will check later with VC++ 2012 to see if they made it work, but right now I have no other explanation than some problems with MS compiler. Or is there any compiler option specifically for this case that I don't know about?
Kosta Cherry wrote:
...size of empty base class should be reduced to zero in descendant if possible.
The size of an empty class must not be zero to ensure that the addresses of two different objects will be different.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
-
Kosta Cherry wrote:
...size of empty base class should be reduced to zero in descendant if possible.
The size of an empty class must not be zero to ensure that the addresses of two different objects will be different.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
Standalone one - yes. But we are talking here about descendants, where empty class/struct can be eliminated. This is called Empty Base Class Optimization (you can google for it).
-
Here is the test code: http://codepad.org/zKTatneG[^] struct Empty { }; struct JustLong { long data; }; struct LongAndEmpty : public JustLong, private Empty { }; struct StringAndEmpty: public std::string, private Empty { }; int main() { cout << "Empty:" << sizeof(Empty) << endl; cout << "JustLong:" << sizeof(JustLong) << endl; cout << "String:" << sizeof(std::string) << endl; cout << "LongAndEmpty:" << sizeof(LongAndEmpty) << endl; cout << "StringAndEmpty:" << sizeof(StringAndEmpty) << endl; return 0; } When you try it in other compiler (for example, see link above for codepad.org), results are correct, i.e. output looks like this: Empty:1 JustLong:4 String:4 LongAndEmpty:4 StringAndEmpty:4 Now, when you try this in VC++ (2010), size of struct with std::string is always (debug and release) 4 more that size of just std::string. Any other structs/classes are of correct size, and only those with std::string are bigger than they should be. Am I missing something or this is MS bug?