Here is the rule of thumb when it comes to the problem of forward declaration or definition:
forward declaration is the Label and definition means opening the box.
Whether the internal information(i.e. size, memory layout, member data/function) of the type are being used, or just its name
class Book; // forward declaration of Book. Right now we know nothing about Book's internal info except it's unique ID, i.e. the name of type: Book.
Book *ptr; // OK, pointer's size is fixed and it has nothing to do with Book.
Book **pptr; // Ok, same with previous
ptr->someDataMember = bla; // ERROR, we don't know what is in a Book object.
ptr->callDataFunc(); // ERROR
++ptr; // ERROR, need to know the sizeof Book, since ++ptr equals to (char*)ptr + sizeof(Book)
++pptr; // OK
*pptr++; // OK, *pptr is a Book*, which size is fixed.
**pptr++; // ERROR
template<typename T>
class DontUseT{};
DontUseT<Book> dut; // OK, we don't use Book's internal information here
int foo(Book *b) { return sizeof(b);} // OK
int foo(Book&b) { return 0;} // OK
int foo(Book&b) { return sizeof(Book);} // ERROR
int foo(Book); // OK
int foo(Book) {} // ERROR, inside the definition it's supposed to access its arguments