Search code examples
c++c++11noncopyablestandard-layout

Standard layout and non-copyable property


C++11, §9/7:

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 and no virtual base classes,
  • has the same access control 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.

So, is there a way to make a class with standard layout non-copyable? If yes, how?

Inheriting privately from boost::noncopyable will not work, because it made copy constructor private (hence not a standard layout). The boost::noncopyable's implementation is like this :

  class noncopyable
  {
   protected:
      noncopyable() {}
      ~noncopyable() {}
   private:  // emphasize the following members are private
      noncopyable( const noncopyable& );
      const noncopyable& operator=( const noncopyable& );
  };

Because of the private section, it is not a standard layout class. I am also note sure if private inheritance break any standard layout rule.


#include <boost/noncopyable.hpp>
#include <iostream>
const int N = 50;
struct A
{
    int data[N];
};
struct B : private boost::noncopyable
{
    int data[N];
};
struct C
{
    A data[10];
};
struct D : private boost::noncopyable
{
    B data[10];
};

int main() {
    std::cout<<sizeof(A)<<std::endl;
    std::cout<<sizeof(B)<<std::endl;

    std::cout<<sizeof(C)<<std::endl;
    std::cout<<sizeof(D)<<std::endl;
}

The output is :

200
200
2000
2004

The example above shows that inheriting privately from boost::noncopyable changes the class into NOT standard layout compliant. I am not sure if this is a g++ bug (I am using g++ 4.6.1), or the standard is somehow violated.


Solution

  • I think there is a confusion here:

    • the standard layout property is affected by attributes (and only attributes)
    • the copyable property is affected by methods (their presence, absence and accessibility)

    The two concepts are orthogonal.

    UPDATE:

    The following display the very same behavior that boost::noncopyable:

    #include <iostream>
    
    struct foo {};
    
    struct B : foo { int data; };
    
    struct D : foo { B data; };
    
    int main() {
      D d;
      std::cout << (char*)(&d.data) - (char*)(&d) << "\n";
    }
    

    The result is 4.

    I believe this is because of:

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

    Indeed, experiment shows that introducing a int a; in D prior to data does not increases its size. I think that the fact that B inherits from foo means that data (first non-static data member) is considered to be the same type as foo (base class of D).

    This leads to an ambiguity: foo* f = &d would have the same address as foo* g = &b.data; if the compiler did not introduce this padding.