Professional C__ - Marc Gregoire [351]
psGrid.setElementAt(1, 1, &x);
Grid Grid psGrid3 = psGrid2; const Grid cout << *(psGrid4.getElementAt(1, 1)) << endl; x=6; cout << *(psGrid4.getElementAt(1, 1)) << endl; Code snippet from GridPartialPtr\GridPtrTest.cpp The psGrid4 grid is storing pointers, so psGrid4.getElementAt(1, 1) will return a pointer to x. Changing the value of the variable x will change the result of dereferencing the pointer returned by getElementAt(1, 1). The output of the above code should be as follows: 3 6 At this point, you’re probably wondering whether this really works. We sympathize with your skepticism. One of the authors was so surprised by this syntax when he first read about it that he didn’t believe it actually worked until he was able to try it out. If you don’t believe us, try it out yourself. Here are the method implementations. Pay close attention to the template line syntax before each method: template Grid mWidth(inWidth), mHeight(inHeight) { mCells = new T** [mWidth]; for (size_t i = 0; i < mWidth; i++) { mCells[i] = new T*[mHeight]; } } template Grid { copyFrom(src); } template Grid { // Free the old memory. for (size_t i = 0; i < mWidth; i++) { delete [] mCells[i]; } delete [] mCells; mCells = nullptr; } template void Grid { mWidth = src.mWidth; mHeight = src.mHeight; mCells = new T** [mWidth]; for (size_t i = 0; i < mWidth; i++) { mCells[i] = new T*[mHeight]; } for (size_t i = 0; i < mWidth; i++) { for (size_t j = 0; j < mHeight; j++) { mCells[i][j] = src.mCells[i][j]; } } } template Grid { // Check for self-assignment. if (this == &rhs) { return *this; } // Free the old memory. for (size_t i = 0; i < mWidth; i++) { delete [] mCells[i]; } delete [] mCells; mCells = nullptr; // Copy the new memory. copyFrom(rhs); return *this; } template void Grid { mCells[x][y] = inElem; } template T* Grid { return mCells[x][y]; } Code snippet from GridPartialPtr\GridPtr.h For brevity, this example is not implementing any deep copying of the pointers. This would be a good exercise for the reader. EMULATING FUNCTION PARTIAL SPECIALIZATION WITH OVERLOADING template size_t Find { for (size_t i = 0; i < size; i++) { if (*arr[i] == *value) { return i; // Found it; return the index } } return NOT_FOUND; // failed to find it; return NOT_FOUND } Code snippet from FunctionTemplatePtr\FindTemplatePtr.cpp However, that syntax declares a partial specialization of the function template, which the C++ standard does not allow. The standard way to implement the behavior you want is to write a new template for Find(): template size_t Find(T*& value, T** arr, size_t size) { for (size_t i = 0; i < size; i++) { if (*arr[i] == *value) { return i; // Found it; return the index } } return NOT_FOUND; // failed to find it; return NOT_FOUND } Code snippet from FunctionTemplatePtr\FindTemplatePtr.cpp The difference might seem trivial and academic, but it makes the difference between portable standard code and code that is not standard compliant. More on Deduction You can define in one program the original Find() template,
The C++ standard does not permit partial template specialization of functions. Instead, you can overload the function with another template. The difference is subtle. Suppose that you want to write a specialization of the Find() function, presented in the previous chapter, that dereferences the pointers to use operator== directly on the objects pointed to. Following the syntax for class template partial specialization, you might be tempted to write this: