1

So I'm having the following files, with the following content:

a.h :

#ifndef A_H
#define A_H

class A {
};

#endif

b.h :

#ifndef B_H
#define B_H

#include <memory>

class A;

class B {
public:
    B(std::unique_ptr<A> a);
private:
    std::unique_ptr<A> a_;
};
#endif

b.cpp :

#include "b.h"
#include "a.h"

B::B(std::unique_ptr<A> a) : a_(std::move(a)) {
}

c.h :

#ifndef C_H
#define C_H

#include "b.h"

class C {
public:
    void add_b(std::unique_ptr<B> b);

private:
    std::unique_ptr<B> b_;
};

#endif

c.cpp:

#include "c.h"

void C::add_b(std::unique_ptr<B> b) {
    b_ = std::move(b);
}

And a file containing an empty main method. As you can see in b.h I'm not actually including a.h since I'm using only pointers in the class declaration and that's not necessary, only a declaration being enough - class A;. If i try to compile this using gcc I'm getting an error that states something like:

In file included from /usr/include/c++/5/memory:81:0, from b.h:4, from c.h:4, from c.cpp:1: error: invalid application of ‘sizeof’ to incomplete type ‘A’
static_assert(sizeof(_Tp)>0

When I saw the part with "incomplete type 'A'" I figured out that class C needs more info about A since it's not aware of it's existence because it's only including b.h which only uses a declaration for A (class A;). Obviously including a.h header in c.cpp solved the error, but then I realized that A is not actually involved in the visible implementation of C class. Why does C need to know more info about class A? What's actually happening? I think this is very obscure and hard to follow. How can I avoid such situations? The original example was much more convoluted so I had to actually dig and simplify this. I was just trying to have a minimal header file with necessary info only, avoiding unnecessary macro expansions.

cuv
  • 1,159
  • 2
  • 10
  • 20

2 Answers2

2

but then I realized that A is not actually involved in the visible implementation of C class. Why does C need to know more info about class A?

Implicitly class A needs to be seen with the implementation of class B as required from

 std::unique_ptr<B> b_;

in class C, hence the error.

It doesn't matter if A is visible by means of the public parts of class B. class B must be fully declared at that point, which includes a full declaration of class A in turn.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
0

You are getting the error because std::unique_ptr is sizeof-ing the underlying incomplete type A. Obviously, to get the sizeof a type, the type has to be defined.

The reason the unique_ptr is sizeof-ing the underlying type is because of std::default_delete<A>. If you override the default deleter for the unique_ptr, you can make it so it doesn't sizeof the underlying type, and you can make it compile without the type being complete.

Example...

#include <memory>
#include <iostream>

class A;

struct Deleter {
    void operator()(A* ptr) const {
        std::cout << "I'm supposed to delete A but I'm a liar!" << std::endl;
    }
};

int main() {
    std::unique_ptr<A, Deleter> a(reinterpret_cast<A*>(1));
}

This compiles, links and runs just fine

pat
  • 763
  • 4
  • 12
  • It compiles fine - but now it's basically just `A*`. – Barry Oct 27 '16 at 19:06
  • Which is precisely what his question was. He was asking why he gets incomplete type errors when he is only using a pointer... This is exactly the reason. – pat Oct 27 '16 at 19:06