Search

13.x — Chapter 13 comprehensive quiz

Templates allow us to write functions or classes using placeholder types, so that we can stencil out identical versions of the function or class using different types. A function or class that has been instantiated is called a function or class instance.

All template functions or classes must start with a template parameter declaration that tells the compiler that the following function or class is a template function or class. Within the template parameter declaration, the template type parameters or expression parameters are specified. Template type parameters are just placeholder types, normally named T, T1, T2, or other single letter names (e.g. S). Expression parameters are usually integral types, but can be a pointer or reference to a function, class object, or member function.

Splitting up template class definition and member function definitions doesn’t work like normal classes -- you can’t put your class definition in a header and member function definitions in a .cpp file. It’s usually best to keep all of them in a header file, with the member function definitions underneath the class.

Template specialization can be used when we want to override the default behavior from the templated function or class for a specific type. If all types are overridden, this is called full specialization. Classes also support partial specialization, where only some of the templated parameters are specialized. Functions do not support partial specialization as of C++14.

Many classes in the C++ standard library use templates, including std::array and std::vector. Templates are often used for implementing container classes, so a container can be written once and used with any appropriate type.

Quiz time

1) It’s sometimes useful to define data that travels in pairs. Write a templated class named Pair1 that allows the user to define one template type that is used for both values in the pair. The following function should work:

and print:

Pair: 5 8
Pair: 2.3 4.5

Show Solution

2) Write a Pair class that allows you to specify separate types for each of the two values in the pair.

Note: We’re naming this class differently from the previous one because C++ does not currently allow you to “overload” classes that differ only in the number or type of template parameters.

The following program should work:

and print:

Pair: 5 6.7
Pair: 2.3 4

Hint: To define a template using two different types, separate the two types by a comma in the template parameter declaration. See lesson 13.1 -- Function templates for more information.

Show Solution

3) A string-value pair is a special type of pair where the first value is always a string type, and the second value can be any type. Write a template class named StringValuePair that inherits from a partially specialized Pair class (using std::string as the first type, and allowing the user to specify the second type).

The following program should run:

and print:

Pair: Hello 5

Hint: When you call the Pair constructor from the StringValuePair constructor, don’t forget to include the template parameters as part of the Pair class name.

Show Solution

14.1 -- The need for exceptions
Index
13.8 -- Partial template specialization for pointers

55 comments to 13.x — Chapter 13 comprehensive quiz

  • Parsa

    Hello,

    I've heard templates are actually a very big topic in  C++, and there is more to it than just this chapter. Where should I look next to learn more about templates?

  • masterOfNothing

    Speaking of templates and inheritance (the last question in the QUIZ):

    Is the StringValuePair a new class template by itself, that inherits from another class template (Pair in this case)?

    Also, the sentence "Write a template class named StringValuePair that inherits from a partially specialized Pair class"

    I translated it as Pair class has to be specialized already, which led to many code errors.

    Does it mean that

    is that partially specialized Pair template class? I only realized this while trying to compose this question.

  • Tommy

    Oof, my understanding of templates is not good. Thankfully I get the first question. Also, thanks to you, I feel so-so comfortable with template functions.
    The second question confuses me though.

    I couldn't find what you were referring to in 13.1 to help explain this. Am I right in thinking that the 2 mentions of "class" are moot here, and are just to refer to the fact that the constructor of this class will take in values of two different types?

    • Alex

      Yes, it's just saying T and S are template parameters where the type is variable. Note that T and S don't need to be different types, they could be the same.

  • Ryan

    I am trying to implement vector functions using arrays.

    Error with T = int. It seems to be on print() but I can't see the problem.
    "'=': cannot convert from 'nullptr' to 'T'"
    " note: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type."

    Also what can I improve in the code?

    • * Use member initializer lists.
      * Use single quotation marks for characters. (\n).
      * Avoid abreviations.
      * You're using the same name style for variables and functions, this can lead to confusion.
      * You're defining some functions inside the class and some outside. Do the same for all unless you have a reason to split them apart.
      * Line 107-117: This can be avoided by adding an initializer list constructor.
      * @operator=: You're swapping, you should assign. @operator= should never modify the right operand.
      * Line 51, 65: Throw an @std::out_of_range exception.
      * Line 67, 70, 89: Your array elements are not pointers.
      * Initialize your variables with brace initializers.
      * Line 101: @expand is void.
      * Line 70, 89: Your array is of type T (int), @nullptr cannot be converted to int. Please post the line numbers along with the error message to make it easier to find the error.

      • Ryan

        thanks. Line 70 error C2440: '=': cannot convert from 'nullptr' to 'T' with
        [
           T=int
        ]
        (70): note: A native nullptr can only be converted to bool or, us

  • That's invalid syntax. Maybe you meant

    But that operator isn't a member of @Stack, so that's wrong too.

    • Anthony

      Now it makes sense. Yes, I was labouring under the delusion that somehow the operator could belong to the class, whereas I am just overloading the operator to accept an argument of that particular class.

      Sorted :)

  • Anthony

    Oh golly. Thanks :) Much appreciated!

    One further thing. In line 20 of classcode.h, we have:

    Which is correct, as it turn out. But why isn't it:

    Is the reason that although the friend declaration is made inside the definition of Stack, this function isn't anything to do with Stack. It is merely a friend function?

  • Anthony

    It's been a challenge getting to grips with template function semantics - or should that be syntax? Probably the hardest part of the course for me so far. I've been reading the Vandevoorde and Josuttis book where they delve deeper.

    Here I'm trying to overload the << operator for a template class with a template as one of the call paramters. I have tried everything I can think of, but it won't compile:

    "undefined reference to `operator<<(std::ostream&, Stack<int, std::vector> const&)'|"

    I'm pretty certain that the friend function must be declared/defined in a slightly different way. Could you possibly take a look?

    classes.h

    classcode.h

    main.cpp

    • Hi Anthony!

      In classes.h:15 you're saying that there's an @operator<< with the same types as the types in @Stack (Because @T and @Tcont are already set at this point). This line serves as a declaration. When the linker later tries to find that function, it won't find it, because it was never generated.
      Your @operator<< is again a template function, with templates independent of those in @Stack, meaning that the declaration in line 15 (Fixed types) doesn't match the definition (Templates).
      To fix this, add another function template before the friend declaration.

      That way the compiler will generate @operator<< when it's called.

      • Anthony

        I have an assignment operator overload as part of the class below. It deals with the case where we have two *different* objects, say, a deque of ints being assigned to a deque of floats. The book I'm learning from includes the cast to void*, but is this really necessary?

        Second, when we explicitly state the 'default' constructor, as I have here, is it still called the 'default' constructor?

        Declaration:

        Definition:

        Sorry about all these questions.

        • > is it still called the 'default' constructor?
          Yes. The "default constructor" is a constructor that's callable without arguments.

          > cast to void*, but is this really necessary?
          @st doesn't necessarily have the same type as @*this. Since you can't compare pointers of different types, you wouldn't be able to compare them.

  • Louis Cloete

    @Alex, you can actually solve Q1 by partially specializing Pair from Q2 as well. I think it will be instructive to reorder the quiz to do so to drive the point of code reuse home. I know from myself that I can understand all the benefits of code reuse in theory, but a few examples on *how* to actually do it can do wonders for my "programmer's instinct" to see opportunities to use language features to actually practically reduce duplication. This is also a good place to show why it is beneficial to separate classes into different files. If you implemented Pair in its own header, you can reuse it easily for Pair1 and StringValuePair, while if it is in main.cpp, you will have to copy and paste around. Here are my solutions:

    pair.h:

    pair1.h:

    stringValuePair.h:

    Anyway, just a suggestion for when you get here in your big "refactoring" of these tutorials.

  • magaji::hussaini

    Hi alex/nascardriver/others...
    Are C++ casts (static_cast<>(), ..., etc) template functons?

  • Joerg

    Hi and thanks for the tutorials :D I feel like the difficulty of the quizzes has fallen down a bit since chapter 9. The time they took me to complete them is only a fraction of what the chapter 9 final quiz took to complete. Is this supposed to be like that?

    • Alex

      These later quizzes need some additional work, but I've been more focused on updating the content than the quizzes. I have it on my to-do to spend some more time on these.

  • Part 3:

  • Part 2:

  • Been a while but now here's my solution to part (1):

  • Max Dmitrachkov

    I don't uderstand "const". On the left. On the right. It's everywhere

  • Arumikaze

    Hello! I have a question about common procedure when writing templates. Is it common procedure to put the function definitions inside the class or outside?

    Is this an acceptable way to write templates?

    Thank you!

    • Hi Arumikaze!

      Unless you're having trouble with multiple classes that depend on each other, write the definitions inside the class. That way you avoid having to write/update everything twice.
      In a previous reply I told you to write definitions in source files, templates are an exception to this.

  • Michael

    Hi Alex,
    I defined StringValuePair as

    and the compiler throws out an error of undefined identifier.

    But in 13.7
    I saw this:

    and it is instantiated using this

    So what's wrong with my StringValuePari definition?

    • Alex

      I'm not sure, there isn't enough information to debug here. Did you include the string header? Did you remember the template <class S> line before the class definition, so it knows that S is a template parameter?

  • Luhan

    I still a little confused about templates with inheritance. Do you know any good source about it?

  • Brad

    In regards to the 3rd question. Omitting the template parameters for the StringVaulePair constructor member initialisation list produces the same result.

    vs

    Would it be correct to assume that this is because of the way the Base class has been defined and that omitting the template parameters should be considered bad form, in this case. While in other cases it can lead to unintended results because you need it for specifying your use of partial specialisation?

    • Alex

      A template class name that doesn't have any template parameters should default to the template parameters defined above it. Since Pair has been previously defined as Pair, Pair should resolve as Pair. However, some compilers don't handle this properly.

      This issue was covered as a defect: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#176

  • Hardik

    How can there be REFERENCES to a FUNCTION?

  • C++ Learner

    why there is a need to write const functions also?

  • Chris

    Why do we have to define (Pair<std::string, S>(key, value)) types in the constructor of StringValuePair even after we already inherited from a defined Pair(class StringValuePair : public Pair<std::string, S>) shouldn't we be able to just call Pair(key, value) ?

    • Alex

      Not sure, the language requires it. Probably to keep the compiler simpler, so it doesn't have to make any inferences about what you mean. I also suppose you could be derived from two different templated versions of a class, in which case the compiler wouldn't be able to make an inference. I can't think of a real use case for doing something like that, but the language doesn't preclude it.

  • Jen

    Hi Alex,
    When you get a chance, would you please elaborate what differences between template<typename xx> and template<class xx> are.
    Thank you.
    -Jen

    • Alex

      There is almost no difference. The only difference is that if you're doing a template of templates, you have to use class (as of C++14). Otherwise class and typename work identically.

  • Hi Alex. Fooling around with the code from exercise 3 I realized something I quite not understand properly. If I remove the const from Pair constructor...

    And consequently from StringValuePair constructor...

    ... I got this error

    'StringValuePair<int>::StringValuePair(StringValuePair<int> &&)': cannot convert argument 1 from 'const char [6]' to 'std::string &'

    I understand that the program fails because "Hello" as a literal is const. Is this the reason why the program fails?

    Thank you!

    • Alex

      The const char[6] literal gets converted to a temporary std::string object, which then gets passed to the T& parameter. Both literals and temporary objects are considered rvalues. However, you can't associate an rvalue with a non-const reference, which is why the compiler is complaining. You can associate a rvalue with a const reference, which is why it works when you include the const.

  • Mauricio Mirabetti

    Alex, one conceptual question, if you please:
    On exercise 1 (by the way, the "1)" is missing on the text), I first defined the getters for "first" and "second" returning const and by value, to serve both const and non-const (p1 and p2) Pair1 objects, like this:

    Your solution returns by reference and creates two version of getters. What is the more correct or usual, two versions of getters by always returning references (your solution), or one version of getter returning const by value?

    Best regards.

    Mauricio

    • Alex

      My first() and second() aren't getters so much as they are accessors (in that they both allow get and set). Your version is close to typical if you intend first() and second() to be read-only -- however, const reference is a better choice here, since m_first and m_second may be class types, and we want to avoid making copies of those. My versions is written more from the viewpoint that we'll allow read-only access for const objects and full read/write access for non-const objects. Allowing direct access to m_first and m_second via reference does violate encapsulation to some degree, so there's that tradeoff. Another way to go would have been to have my first() and second() return by const reference only, and then provide a setFirst() and setSecond() for non-const objects that allowed the user to set the value of m_first and m_second. That's probably the safest choice all-around.

  • Nathan

    Alex, FYI - cannot compile .h files using clang++

  • Daniel

    Hi Alex,
    One small question with quiz3.
    Copy Quiz3 to compile will get following errors:
    main_quiz3.cpp: In constructor ‘StringValuePair<S>::StringValuePair(const string&, const S&)’:
    main_quiz3.cpp:28:5: error: class ‘StringValuePair<S>’ does not have any field named ‘Pair’
       : Pair(key, value)
         ^
    main_quiz3.cpp: In instantiation of ‘StringValuePair<S>::StringValuePair(const string&, const S&) [with S = int; std::string = std::basic_string<char>]’:
    main_quiz3.cpp:35:37:   required from here
    main_quiz3.cpp:28:20: error: no matching function for call to ‘Pair<std::basic_string<char>, int>::Pair()’
       : Pair(key, value)
                        ^
    main_quiz3.cpp:28:20: note: candidates are:
    main_quiz3.cpp:12:2: note: Pair<T, S>::Pair(const T&, const S&) [with T = std::basic_string<char>; S = int]
      Pair(const T& x, const S& y)
      ^
    main_quiz3.cpp:12:2: note:   candidate expects 2 arguments, 0 provided
    main_quiz3.cpp:5:7: note: Pair<std::basic_string<char>, int>::Pair(const Pair<std::basic_string<char>, int>&)
    class Pair
           ^
    main_quiz3.cpp:5:7: note:   candidate expects 1 argument, 0 provided

    change line 28 to explicitly define can pass compile.
    -> 28         : Pair<std::string, S>(key, value)

Leave a Comment

Put all code inside code tags: [code]your code here[/code]