Template Specialization in C++ - dummies

By John Paul Mueller, Jeff Cogswell

Some templates don’t go together quite as easily as you might expect because they express a concept that doesn’t translate the same way for every data type. For example, when you use stringify to turn a data type into its string representation, the technique differs based on data type. For example, when you want to use stringify on an int, you might use the following template:

#include <iostream>
#include <sstream>
using namespace std;
template<typename T> inline string stringify(const T& input)
{
    ostringstream output;
    output << input;
    return output.str();
}
int main()
{
    // This call works as expected.
    cout << stringify<int>(42) << endl;
    // This call truncates.
    cout << stringify<double>(45.6789012345) << endl;
    return 0;
}

The stringify() function accepts any data type and simply uses an ostringstream to convert input to a string. This approach works fine for the first call in main(), which is an int. However, when the code uses it for a double, the result is truncated as shown here:

42
45.6789

You can fix this problem by adding special handling for a double. Here is the modified form of the example (as shown in StringifyDouble) that accommodates a double.

#include <iostream>
#include <sstream>
#include <iomanip>
#include <limits>
using namespace std;
template<typename T> inline string stringify(const T& input)
{
    ostringstream output;
    output << input;
    return output.str();
}
template <> inline string stringify<double> (const double& input)
{
    ostringstream output;
    const int sigdigits = numeric_limits<double>::digits10;
    output << setprecision(sigdigits) << input;
    return output.str();
}
int main()
{
    cout << stringify<int>(42) << endl;
    cout << stringify<double>(45.6789012345) << endl;
    return 0;
}

When you run this example, you see the expected result because the double form of the template uses setprecision to modify the ostringstream value. As a result, you see the following output:

42
45.6789012345

As things sit with C++ today, you must create a special template for each data type that requires it. Theoretically, if C++ ever gets a typeof() function, you could detect the data type and add a switch to perform specialized processing within a single template.

The typeid() function could work as a substitute for typeof(), but it’s vendor-specific and not implemented in every version of C++. If you choose to use the typeid() function, make sure you know which compiler your organization will use to compile the application code.

You may have also noticed the inline keyword used for the template in this example. The inline keyword tells the compiler to place the code created by the template in line with the code in which it appears, rather than out of line as a separate function call. In some cases, such as this stringify() function, the result is code that executes faster.

The compiler is under no obligation to comply with the inline keyword. In addition, you want template code placed out of line when it must perform some level of instantiation or it doesn’t represent critical path code that the application can call often.