10

Suppose I have class with no data:

struct Empty {
  /*some methods here*/
};

And a derived class

struct Derived: Empty {
  int a;
  int b;
  char c;
  ....
}__attribute__((packed));`

Objects of Empty class have size = 1. Empty part of derived class usually has 0 size. As I understand compiler see that base Empty class has no data so it can optimize size of Empty in case it is "inside" Derived but it is not required to do it by the standard.

So the question is:

Can I somehow determine at compile time that Empty part of Derived class doesn't really occupy memory.

I understand that I can do check like sizeof(Derived) = sizeof(a) + sizeof(b) ... But It is too verbose and there are several classes like Derived. Is there more elegant solution?

Seleznev Anton
  • 641
  • 5
  • 14
  • Why do you want to know that? Note that a member or base class can occupy memory without adding to the footprint of the derived class (by using space lost to padding). Also note that `sizeof` on a struct can be less or more than the sum of the `sizeof` of it's member and bases. – skyking Jan 16 '17 at 10:22
  • I'm going to use these Derived classes to represent some network data. So all such Derived classes will have packed attribute. Also I'm going to inherit from some template class to implement Curiously Recurring Template Pattern. So that all derived classes will have some common functionality. However I don't want that this inheritance affects the layout of Derived classes. – Seleznev Anton Jan 16 '17 at 10:58

2 Answers2

12

You can use std::is_empty to make sure that the class you're inheriting from is zero-sized:

static_assert(std::is_empty<Empty>{});

If it is, empty base optimization is guaranteed to take place for standard-layout classes.


I understand that I can do check like sizeof(Derived) = sizeof(a) + sizeof(b) ... But It is too verbose. Is there more elegant solution?

This does not work properly because you need to take padding into account and eventual attributes such as packed.

Community
  • 1
  • 1
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Thanks. Didn't know this optimization is guaranteed. – Seleznev Anton Jan 16 '17 at 09:42
  • The standard does not guarantee this optimisation. It is pretty common in practice though with real-world compilers. – Peter Jan 16 '17 at 09:55
  • 1
    @Peter: [it is guaranteed for *standard-layout* classes](http://stackoverflow.com/questions/10788823/is-the-empty-base-class-optimization-now-a-mandatory-optimization-at-least-for). I'll clarify my answer. – Vittorio Romeo Jan 16 '17 at 09:58
3

You can use more "old" (before C++11) macro - offsetof:

struct Empty {};
struct NonEmpty {
  int a;
};
struct Derived1: Empty {
  int a;
  int b;
  char c;
};
struct Derived2: NonEmpty {
  int a;
  int b;
  char c;
};
static_assert(offsetof(Derived1,a) == 0,"");
static_assert(offsetof(Derived2,a) != 0,"");

You can use this macro to check order of your member variables too:

static_assert(offsetof(Derived,a) < offsetof(Derived,b),"");
static_assert(offsetof(Derived,b) < offsetof(Derived,c),"");

But do not forget - offsetof has the same limitation:

If type is not a standard layout type, the behavior is undefined. If member is a static member or a member function, the behavior is undefined.

chi
  • 111,837
  • 3
  • 133
  • 218
SergV
  • 1,269
  • 8
  • 20