views:

150

answers:

3

Hi all,

I made a private API that assumes that the address of the first member-object in the class will be the same as the class's this-pointer... that way the member-object can trivially derive a pointer to the object that it is a member of, without having to store a pointer explicitly.

Given that I am willing to make sure that the container class won't inherit from any superclass, won't have any virtual methods, and that the member-object that does this trick will be the first member object declared, will that assumption hold valid for any C++ compiler, or do I need to use the offsetof() operator (or similar) to guarantee correctness?

To put it another way, the code below does what I expect under g++, but will it work everywhere?

class MyContainer
{
public:
   MyContainer() {}
   ~MyContainer() {}  // non-virtual dtor

private:
   class MyContained
   {
   public:
      MyContained() {}
      ~MyContained() {}

      // Given that the only place Contained objects are declared is m_contained
      // (below), will this work as expected on any C++ compiler?
      MyContainer * GetPointerToMyContainer()
      {
         return reinterpret_cast<MyContainer *>(this);
      }
   };

   MyContained m_contained;  // MUST BE FIRST MEMBER ITEM DECLARED IN MyContainer
   int m_foo;                // other member items may be declared after m_contained
   float m_bar;
};
+2  A: 

It is not guaranteed for non-POD types. C++ Standard 9.2/12:

Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allo- cated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (11.1). Implementation alignment require- ments might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).

In your case you have non-POD type since it contains custom destructor. More about POD types you could read here.

Kirill V. Lyadvinsky
Although there are some guarantees for PODs, which I think include what OP asks for.
avakar
OP asks about non-POD type since type-in-question has custom destructor.
Kirill V. Lyadvinsky
Still, what exactly says that it is not guaranteed, given no vtable / bases / intervening access specifiers?
UncleBens
This says there can be padding *between* members, not at the beginning. (Probably the kind of underspecification leading to the "standard-layout class" notion in the standard for C++0x?)
UncleBens
It doesn't say it is guaranteed, so it is not guaranteed. At the same time there is an explicit guarantee for POD types.
Kirill V. Lyadvinsky
+4  A: 

It seems the current standard guarantees this only for POD types.

9.2.17

A pointer to a POD-struct object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [Note: There might therefore be unnamed padding within a POD-struct object, but not at its beginning, as necessary to achieve appropriate alignment. ]

However, the C++0x standard seems to extend this guarantee to "standard-layout struct object"

A standard-layout class is a class that:

— has no non-static data members of type non-standard-layout class (or array of such types) or reference,

— has no virtual functions (10.3) and no virtual base classes (10.1),

— has the same access control (Clause 11) for all non-static data members,

— has no non-standard-layout base classes,

— either has no non-static data members in the most-derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and

— has no base classes of the same type as the first non-static data member.

A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class.

It is probably likely that the assumption holds in practice (and the former didn't just have these distinctions, though this could have been the intention)?

UncleBens
+1  A: 

The latest C++ spec draft says this is ok, as long as the class qualifies as a standard layout class, which just requires

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  • has no virtual functions (10.3) and no virtual base classes (10.1),
  • has the same access control (Clause 11) for all non-static data members,
  • has no non-standard-layout base classes,
  • either has no non-static data members in the most-derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  • has no base classes of the same type as the first non-static data member.

Depending on the definition of MyContained, your class might or might not be standard layout

Note that POD-classes are the intersection of standard layout and trivially copyable classes

Chris Dodd