2

I have a class that looks like this:

// List.hpp
template<typename T>
class List {
    ...
private:
    class Node;
};

And I want to put the full definition of List<T>::Node into a separate file:

// Node.hpp
template<typename T>
class List<T>::Node {
    ...
};

My question is, what should I include in each file, and where to put the #includes?

The simplest method (I think) is to include Node.hpp at the end of List.hpp, and not include anything in Node.hpp. However, this makes Node.hpp on its own not a complete file (say if I open it in an IDE or something there would be a lot of errors due to missing the definition of List<T>). Also, I'm not sure if it's okay to put #includes at the bottom of a file.

Assuming each file has its own include guard #define, I can also include List.hpp at the top of Node.hpp (to make IDEs happy), and then include Node.hpp at the bottom of List.hpp again, but I don't know if that's a good idea.

Zizheng Tai
  • 6,170
  • 28
  • 79
  • Adressing parts of your question: You can put an `#incldue` anywhere you want and I don't see, how it would help you, if you include the same file twice. – MikeMB May 06 '16 at 21:50
  • You can do what you suggest -- you could also selectively include it in other .h or .cpp files as needed. This can reduce the compilation blast radius of changing node.hpp to only the places that actually use it, instead of everything that includes list.hpp – xaxxon May 06 '16 at 21:54
  • also, here's a live example of this arrangement working: https://godbolt.org/g/5c4HC7 – xaxxon May 06 '16 at 21:55
  • 1
    semi-offtopic: If I have a lot of templated code in a .h file that's getting changed a lot, I'll often move it from filename.h to filename_impl.h and then only include it in the .cpp files that actually use that templated method. It stops a large portion of the project from detecting the change and rebuilding when they only needed the type info, not the implementation details. If you miss a spot, the compiler gives a pretty good link error telling you were to include the _impl. – xaxxon May 06 '16 at 21:57
  • @MikeMB *...if you include the same file twice.* Having `Node.hpp` include `List.hpp` at its top makes `Node.hpp` on its own a complete compilation unit. – Zizheng Tai May 06 '16 at 21:58

1 Answers1

4

When compiling member functions of List<T>::Node, the compiler will always need visibility of the templated class List. A consequence of this is that any compilation unit which needs visibility of the contents of Node.hpp will need visibility of the contents (definition of the templated members) of List.hpp.

The simplest option is placing the contents of Node.hpp at the end of List.hpp, and only #include "List.h" in compilation units that need either template. That eliminates the need for Node.hpp completely.

If you really must have both headers (which seems arbitrary and pointless to me, as List<T>::Node is a component of List<T>) an option is to help with include guards.

  // List.hpp

  #ifndef LIST_HPP_INCLUDED   // some unique macro
  #define LIST_HPP_INCLUDED

  // definition of templated class List

  #include "Node.hpp"   // this must be after the definition of List<T>
  #endif

and in Node.hpp

  // Node.hpp

  #ifndef NODE_HPP_INCLUDED   // some unique macro
  #define NODE_HPP_INCLUDED

  #include "List.hpp"     // this must precede the definitions for List<T>::Node

  // definition of templated class List<T>::Node

  #endif

This allows either header to be #included by any compilation units, and the include guards stop infinitely recursive includes.

Most modern IDEs do cope with this. If you have one that doesn't, then simply transition to a single header file.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • I wanted to separate `Node` to its own file just to make `List.hpp` cleaner, but as you say it's not necessary. – Zizheng Tai May 06 '16 at 22:00
  • Also, if I do as you suggest (putting `class List::Node { ... }` at the end of `List.hpp`), can't I just as well define `Node` inside `List` directly? – Zizheng Tai May 06 '16 at 22:01
  • Well, yes. `Node` is a nested class of `List`. The definition of any nested class - templated or not - can be placed within the definition of the containing class. – Peter May 06 '16 at 23:10