Professional C__ - Marc Gregoire [339]
template Grid void setElementAt(size_t x, size_t y, const T& inElem); T& getElementAt(size_t x, size_t y); const T& getElementAt(size_t x, size_t y) const; size_t getHeight() const { return HEIGHT; } size_t getWidth() const { return WIDTH; } protected: template void copyFrom(const Grid T mCells[WIDTH][HEIGHT]; }; Code snippet from MethodTemplatesNonType\Grid.h This new definition includes method templates for the copy constructor and assignment operator, plus a helper method copyFrom(). When you write a copy constructor, the compiler stops generating a default constructor for you (details can be found in Chapter 6), so you have to add a default constructor as well. Note, however, that you do not need to write non-templatized copy constructor and assignment operator methods because the compiler-generated ones continue to be generated. They simply copy or assign mCells from the source to the destination, which is exactly the semantics you want for two grids of the same size. When you templatize the copy constructor, assignment operator, and copyFrom(), you must specify all three template parameters. Here is the templatized copy constructor: template template Grid { copyFrom(src); } Code snippet from MethodTemplatesNonType\Grid.h Here are the implementations of copyFrom() and operator=. Note that copyFrom() copies only WIDTH and HEIGHT elements in the x and y dimensions, respectively, from src, even if src is bigger than that. If src is smaller in either dimension, copyFrom() pads the extra spots with zero-initialized values. T() calls the default constructor for the object if T is a class type, or generates 0 if T is a simple type. This syntax is called the zero-initialization syntax. It’s a good way to provide a reasonable default value for a variable whose type you don’t yet know: template template void Grid { for (size_t i = 0; i < WIDTH; i++) { for (size_t j = 0; j < HEIGHT; j++) { if (i < WIDTH2 && j < HEIGHT2) { mCells[i][j] = src.getElementAt(i, j); } else { mCells[i][j] = T(); } } } } template template Grid const Grid { // No need to check for self-assignment because this version of // assignment is never called when T and E are the same // No need to free any memory first // Copy the new memory. copyFrom(rhs); return *this; } Code snippet from MethodTemplatesNonType\Grid.h Template Class Specialization You can provide alternate implementations of class templates for specific types. For example, you might decide that the Grid behavior for char*s (C-style strings) doesn’t make sense. The grid currently stores shallow copies of pointer types. For char*’s, it might make sense to do a deep copy of the string. Alternate implementations of templates are called template specializations. Again, the syntax is a little weird. When you write a template class specialization, you must specify that it’s a template, and that you are writing the version of the template for that particular type. Here is the syntax for specializing the original version of the Grid for char*s: // When the template specialization is used, the original template must be // visible too. #including it here ensures that it will always be visible // when this specialization is visible. #include "Grid.h" template <> class Grid { public: Grid(size_t inWidth = kDefaultWidth, size_t inHeight = kDefaultHeight); Grid(const Grid