The purpose of this quiz is to give you a chance to focus your knowledge of making template classes in C++.
Given the class below, show how you might usefully make it into a template.
class Greater { public: bool operator () (long x, long y) const { return x > y; } };
template <CompType> class Greater { public: bool operator () (const CompType & x, const CompType & y) const { return x > y; } };
When making a class into a template, the class' name is no longer the name of the data type. What is the data type's official name? What about after you've instantiated it to create an object: what is the type of that object?
Use the above class as an illustrative example.
The name of the type is now the class name augmented with angle brackets and the template typename(s) inside them. For example, in the first question, the type isn't Greater but rather Greater<CompType>. After the class has been instantiated, the angle brackets are filled with the instantiation type. So the type of this object: Greater<double> comp_dbl; is going to be Greater<double>.
TRUE ✗ | FALSE ✓ | template classes can easily be separated into interface and implementation to form a library. | ||
---|---|---|---|---|
TRUE ✗ | FALSE ✓ | Instantiating a template class doesn't require any knowledge on the compiler's part of the definitions of the class' methods. | ||
TRUE ✗ | FALSE ✓ | This is because the only types that can be filled into the template are built-in types. Other classes cannot be used to fill in the data type blank of a template class. | ||
TRUE ✓ | FALSE ✗ | When defining the (non-inline) methods of a class, the type to the left of the scope operator must represent the template class' data type name — not the simple class name. |
Show the steps taken by the compiler to test instantiation of the following template class. (Hint: Requirements list..? Each type..? Operations used..?) (Note: some attempted instantiations are given but they are there just to guide you. You needn't actually show if they work or their instantiated names. It would be nice and good practice for you, but not necessary.)
template <typename SumType, typename ItemType> class Sum { SumType sum; public: Sum(const ItemType & s = ItemType()) { sum = s; } Sum(const Sum & s) { sum = s.sum; } SumType operator() (const ItemType & n) { return sum += n; } SumType operator() (void) { return sum; } void reset(const ItemType & s = ItemType()) { sum = s; return; } }; // potential instantiations Sum<double,double> s_dd; Sum<double,long> s_dl(10); Sum<Date,long> s_Dl; Sum<Complex,Complex> s_CC = 0.0; Sum<string,char> s_Sc;
SumType: needs: i) assignment from ItemType ii) assignment from itself iii) += from ItemType iv) copy construction ItemType: needs: i) default construction s_dd: SumType as double, ItemType as double: ST: i) double = double, check ii) double = double, check iii) double += double, check iv) copy double, check IT: i) default construct double, check (not normally used, but there) s_dl(10): SumType as double, ItemType as long: ST: i) double = long, check (warnings on 64-bit long machines) ii) double = double, check iii) double += long, check (warnings on 64-bit long machines) iv) copy double, check IT: i) default construct long, check (not normally used, but there) s_Dl: SumType as Date, ItemType as long: ST: i) Date = long, check (this should be a basic constructor) ii) Date = Date, check iii) Date += long, check (probably, if designed well) iv) copy Date, check IT: i) default construct long, check (not normally used, but there) s_Sc: SumType as string, ItemType as char: ST: i) string = char, fails (this should be a basic constructor, but you have to precede the char with a count) ii) string = string, check iii) string += char, check iv) copy string, check IT: i) default construct char, check (not normally used, but there)
Explain the strange three-stage syntax involved in making a function a friend of a template class.
i) declare the template class ii) declare the function to make friends with iii) inside the template class declare your friendship with the other function; follow the name with empty angle brackets to remind the compiler that this is the templated function declared earlier
Show the interface, implementation, and definition files for the following template class. (You must have at least one definition not inline.)
/* * A Pair is of the form: (x,y) or [x..y] or { x y }. * * In general, there is a prelude symbol, a separator * symbol, a postlude symbol, in addition to the two * data items themselves. */ template <typename FirstType, typename SecondType, typename DescType> class Pair { FirstType first; SecondType second; DescType pre, sep, post; public: Pair(void); Pair(const FirstType & f, const SecondType & s); Pair(const FirstType & f, const SecondType & s, const DescType & Pre, const DescType & Sep, const DescType & Post); Pair(const Pair & p); FirstType get_first(void) const; SecondType get_second(void) const; void set_first(const FirstType & f); void set_second(const SecondType & s); void set_prelude(const DescType & d); void set_separator(const DescType & d); void set_postlude(const DescType & d); friend ostream & operator << (ostream & out, const Pair & p); friend istream & operator >> (istream & in, Pair & p); };
The interface file (pair.h) would be almost the above except for these lines around it: #ifndef PAIR_H_INC #define PAIR_H_INC template <typename FirstType, typename SecondType, typename DescType> class Pair; template <typename FirstType, typename SecondType, typename DescType> ostream & operator << (ostream & out, const Pair<FirstType, SecondType, DescType> & p); template <typename FirstType, typename SecondType, typename DescType> istream & operator >> (istream & in, Pair<FirstType, SecondType, DescType> & p); // class definition goes here // add <> after each friend operator's name inside: //friend ostream & operator << <> (ostream & out, const Pair & p); //friend istream & operator >> <> (istream & in, Pair & p); #ifdef TEMPL_CANT_SEP #include "pair.defn" #endif #endif The implementation file (pair.cpp) would be nearly empty consisting only of: #ifndef TEMPL_CANT_SEP #include "pair.h" #include "pair.defn" #endif The definitions file (pair.defn) would be all the member functions' and friends' definitions that we didn't decide to inline. Here I've made just the default constructor non-inline for exposition of the idea: Pair<FirstType, SecondType, DescType>::Pair(void) : first{}, second{}, pre{}, sep{}, post{} { } Note that there are no #includes here and no using directive allow as it might be #include'd into the .h file! The other functions would have been inline'd inside the definition in the interface file.
Describe the two methods that can be used to include a template class library in an application when the compiler has not yet realized how to separate templates into multiple files. How do you decide which method to use? Which do you feel is better?
We can either use a #define before each #include of the .h file: #define TEMPL_CANT_SEP #include "pair.h" Or, we could use the -D command-line parameter to define the symbol we've chosen for all separately compiled units: CPP -DTEMPL_CANT_SEP main_app other_lib something_else If you are using the template library in multiple other files, it is far easier to use the -D on the command-line once. But if just doing a one-off use, the #define method can be easier and clearer.