none
What happened to the empty base class optimization ?!

    Question

  • We are porting our project to VS2010, and ran into a problem where a class is designed to exactly overlay a char* but, being a class, has an operator== and other functions.  Suppose it's called class cstr and contains a single data member of type char*.  Consider code like this:

    void foo (char** oldfashondarray) {
    cstr* begin= reinterpret_cast<cstr*>(oldfashondarray);
    std::find ( ... begin ...);  // uses cstr::operator== which is strcmp
    }

    Anyway, under VS2010, this no longer works.  Investigation shows that sizeof(cstr) is now 0x10, while under VS2008 it was 8 and == sizeof(char*) (this is a 64-bit build.)

    The class looks something like this:

    struct cstr : D<char> {
    // a bunch of typedefs for metaprogramming support
    // definition for operator== and others
    // but no data members defined
    };

    and the more generic base class:

    template <typename T>
    struct D : std::random_access_iterator_tag {
    // a bunch of typedefs and an accessor
    T* pointer;  // the only data member
    };

    The pointer member in the final cstr class is at offset 8, not offset 0, so *something* is taking up room before that. 

    I surmise that the empty base class optimization is not working at some point within or at the std::random_access_iterator_tag.  Since the morphology is   random_access_iterator_tag : bidirectional_iterator_tag : forward_iterator_tag and that is derived from two empty bases itself (input_iterator_tag, output_iterator_tag), I suspect the multiple inheritance.

    Anyway, the final class was always right before; that is, the base class D and everything above it collapsed down to nothing.  It changed in VS2010.

    Can someone shed light on this?  Is there a compiler setting to restore the old behavior?  Trying to locate and fix everyplace this is being done would be a big problem.

    Thanks,
    --John

     

    Tuesday, December 07, 2010 9:43 PM

Answers

  • The link you mentioned indicates that empty base optimization is indeed performed on the first base only, and has always been that way.  So why did the class size change?  It appears that it's not a change in code generation, but a change in the STL library source code.  In previous versions (in <xutility>)

     

    struct forward_iterator_tag
      : public input_iterator_tag
      {  // identifying tag for forward iterators
      };
    
    

    While in VS2010, this struct has multiple bases

      : public input_iterator_tag, output_iterator_tag
    

    Contemplating how your suggested workaround would perform in the more complex situations, I realize that the class D is quite strange!  Normally iterators don't derive from the tag, but use the tag typedef as a member. 

    Yet this weird code (written by someone who is no longer with the company) is indeed sensitive to this, as removing the tag base makes it not compile.

    I'll remember that work-around for future reference.  Thanks.

    Wednesday, December 08, 2010 5:09 PM

All replies

  • Did cstr somehow get a vtable?

    Tuesday, December 07, 2010 10:12 PM
  • This issue was reported here: https://connect.microsoft.com/VisualStudio/feedback/details/100686/empty-member-optimization-not-working-properly.

     

    Check if this workaround makes any sense:

     

    template< typename T >

    struct D

    {

        T * pointer;

     

        operator std::random_access_iterator_tag ()

        {

            return std::random_access_iterator_tag();

        }

    };

     

    Wednesday, December 08, 2010 7:35 AM
  • The link you mentioned indicates that empty base optimization is indeed performed on the first base only, and has always been that way.  So why did the class size change?  It appears that it's not a change in code generation, but a change in the STL library source code.  In previous versions (in <xutility>)

     

    struct forward_iterator_tag
      : public input_iterator_tag
      {  // identifying tag for forward iterators
      };
    
    

    While in VS2010, this struct has multiple bases

      : public input_iterator_tag, output_iterator_tag
    

    Contemplating how your suggested workaround would perform in the more complex situations, I realize that the class D is quite strange!  Normally iterators don't derive from the tag, but use the tag typedef as a member. 

    Yet this weird code (written by someone who is no longer with the company) is indeed sensitive to this, as removing the tag base makes it not compile.

    I'll remember that work-around for future reference.  Thanks.

    Wednesday, December 08, 2010 5:09 PM