#include #include #include /*************************************************** * ADVANCED TOPIC * * THIS IS NOT REQUIRED FOR THIS COURSE. * * In the previous exercise on clamping, we defined * four functions: * * clamp_int(...) * clamp_uint(...) * clamp_short(...) * clamp_ushort(...) * * The bodies of all four functions were identical. * The only differences were the types of the * parameters and the return value. * * For example, we had essentially * * int clamp_int( int n, * int lower, * int upper ) { * ... * } * * and * * short clamp_short( short n, * short lower, * short upper ) { * ... * } * * It would be convenient if we could write the * algorithm only once and ask the compiler to create * the appropriate function for whatever type we need. * * C++ allows us to do exactly this using a * * function template. * * * A TYPE AS A PARAMETER * * Consider the declaration * * template * TYPE clamp( TYPE n, * TYPE lower, * TYPE upper ); * * The first line says that TYPE represents a type * that will be determined when the function is used. * * You can think of TYPE as being similar to a * parameter. Ordinary function parameters represent * values: * * int square( int n ); * ^ * | * n represents * a value * * A template parameter can instead represent a type: * * template * ^ * | * TYPE represents * a type * * For example, if we write * * clamp( ... ) * * then TYPE is 'int'. You can imagine the compiler * replacing TYPE by int throughout the template: * * int clamp( int n, * int lower, * int upper ) { * ... * } * * If we instead write * * clamp( ... ) * * then TYPE is 'unsigned short', and the compiler can * create the corresponding function: * * unsigned short clamp( * unsigned short n, * unsigned short lower, * unsigned short upper * ) { * ... * } * * We therefore write the clamping algorithm only * once, while the compiler creates the versions that * are actually required by our program. * * * TEMPLATE ARGUMENT DEDUCTION * * Frequently, we do not even have to tell the * compiler which type to use. * * Suppose that * * int n{ 42 }; * * and we call * * clamp( n, 0, 100 ) * * All three arguments have type 'int', so the * compiler can determine that TYPE must be 'int'. * * Thus, * * clamp( n, 0, 100 ) * * and * * clamp( n, 0, 100 ) * * call the same version of the function. * * For this example, we will initially write the type * explicitly so that it is clear which version of * the function template we are asking the compiler * to create. * * * ONE IMPORTANT RESTRICTION * * Our template declares all three parameters using * the same TYPE: * * TYPE clamp( TYPE n, * TYPE lower, * TYPE upper ) * * Consequently, it is simplest if all three arguments * have the same type. * * Templates can become considerably more sophisticated * than this, but those details are beyond what we need * for this example. ***************************************************/ // Function declarations int main(); template TYPE clamp( TYPE n, TYPE lower, TYPE upper ); // Function definitions int main() { int m1{ 23532235 }; int m2{ 2353 }; int m3{ -53213512 }; std::cout << "Clamp to the range of an unsigned short:" << std::endl; std::cout << clamp( m1, 0, std::numeric_limits::max() ) << std::endl; std::cout << clamp( m2, 0, std::numeric_limits::max() ) << std::endl; std::cout << clamp( m3, 0, std::numeric_limits::max() ) << std::endl; std::cout << std::endl; std::cout << "Clamp to the range of a short:" << std::endl; std::cout << clamp( m1, std::numeric_limits::min(), std::numeric_limits::max() ) << std::endl; std::cout << clamp( m2, std::numeric_limits::min(), std::numeric_limits::max() ) << std::endl; std::cout << clamp( m3, std::numeric_limits::min(), std::numeric_limits::max() ) << std::endl; std::cout << std::endl; unsigned short n1{ 5331 }; unsigned short n2{ 35 }; unsigned short n3{ 2126 }; std::cout << "Clamp to the range of an unsigned char:" << std::endl; std::cout << clamp( n1, 0, std::numeric_limits::max() ) << std::endl; std::cout << clamp( n2, 0, std::numeric_limits::max() ) << std::endl; std::cout << clamp( n3, 0, std::numeric_limits::max() ) << std::endl; std::cout << std::endl; std::cout << "Clamp to the range of a signed char:" << std::endl; std::cout << clamp( n1, 0, std::numeric_limits::max() ) << std::endl; std::cout << clamp( n2, 0, std::numeric_limits::max() ) << std::endl; std::cout << clamp( n3, 0, std::numeric_limits::max() ) << std::endl; return 0; } // TYPE clamp( ... ) // // Clamp n to the interval from lower through upper. // // The type TYPE is a template parameter. The compiler // substitutes an appropriate type for TYPE when this // function template is used. // // For example, // // clamp( ... ) // // creates and calls a version in which TYPE is int, // while // // clamp( ... ) // // creates and calls a version in which TYPE is // unsigned short. // // The algorithm itself only has to be written once. template TYPE clamp( TYPE n, TYPE lower, TYPE upper ) { assert( lower <= upper ); if ( n <= lower ) { return lower; } else if ( n >= upper ) { return upper; } else { return n; } }