Chapter 10: Basic Template Terminology_《C++ Templates》notes

发布于:2025-03-22 ⋅ 阅读:(81) ⋅ 点赞:(0)


Key Concepts & Code Walkthrough


  1. Class Template vs. Template Class
  • Class Template: Blueprint for generating classes. Not a real class.
  • Template Class: Actual class generated by instantiating a class template.
#include <iostream>

// CLASS TEMPLATE
template<typename T>
class Box {
    T content;
public:
    Box(T c) : content(c) {}
    T get() const { return content; }
};

int main() {
    Box<int> intBox(42);        // Template class Box<int>
    Box<std::string> strBox("Hello"); // Template class Box<std::string>
    
    std::cout << intBox.get() << "\n";   // 42
    std::cout << strBox.get() << "\n";   // Hello
}

  1. Substitution, Instantiation, Specialization
  • Substitution: Replacing template parameters with concrete types during compilation.
  • Instantiation: Generating actual code from a template.
  • Explicit Specialization: Custom implementation for specific types.
// Primary template
template<typename T>
void printType() { std::cout << "Generic\n"; }

// Explicit specialization for int
template<>
void printType<int>() { std::cout << "int\n"; }

int main() {
    printType<double>();  // Instantiates primary template: "Generic"
    printType<int>();      // Uses specialization: "int"
}

  1. Declaration vs. Definition
  • Templates must be defined in headers (ODR rules).
// mytemplate.h
template<typename T>
T add(T a, T b) { return a + b; } // Declaration & definition together

// main.cpp
#include <iostream>
#include "mytemplate.h"

int main() {
    std::cout << add(3, 4) << "\n";    // 7 (int version instantiated)
    std::cout << add(2.5, 3.5) << "\n";// 6.0 (double version)
}

  1. One-Definition Rule (ODR)
  • A template can appear in multiple translation units but must be identical.
// header.h
template<typename T>
T max(T a, T b) { return (a > b) ? a : b; }

// unit1.cpp
#include "header.h"
void foo() { max(10, 20); }

// unit2.cpp
#include "header.h"
void bar() { max(3.14, 2.71); }

// Linker sees two instantiations: max<int> and max<double> (no conflict)

  1. Template Parameters vs. Arguments
  • Parameters: Placeholders in template definitions (T in template<typename T>).
  • Arguments: Concrete types/values used during instantiation (int in Box<int>).
template<typename T, int N> // T and N are parameters
class Array {
    T data[N];
public:
    T& operator[](int idx) { return data[idx]; }
};

int main() {
    Array<double, 5> arr; // double and 5 are arguments
    arr[0] = 3.14;
    std::cout << arr[0] << "\n"; // 3.14
}

  1. SFINAE (Substitution Failure Is Not An Error)
  • Invalid substitutions are silently removed from overload resolution.
#include <iostream>

// Version 1: Enabled if T has a nested type 'type'
template<typename T, typename = typename T::type>
void test(int) { std::cout << "Has nested type\n"; }

// Version 2: Catch-all
template<typename T>
void test(...) { std::cout << "No nested type\n"; }

struct Foo { using type = int; };

int main() {
    test<Foo>(0);  // Calls version 1
    test<int>(0);   // Calls version 2 (SFINAE discards version 1)
}

Key Takeaways

  1. Class Template is a blueprint; Template Class is the instantiated result.
  2. Instantiation happens implicitly when you use a template with concrete types.
  3. Always define templates in headers to satisfy the ODR.
  4. SFINAE allows graceful handling of invalid substitutions in overload resolution.

Each code example includes a main() function for immediate testing. Compile with:

g++ -std=c++17 example.cpp -o example && ./example

Multiple-Choice Questions (Hard Difficulty)

  1. Which of the following statements about template argument deduction are correct?

    • A. Template argument deduction considers implicit conversions.
    • B. Deduction fails if the template parameter is a non-deduced context.
    • C. decltype(auto) as a return type always deduces to a reference.
    • D. SFINAE can prevent substitution failures from causing compilation errors.
  2. In the context of two-phase name lookup for templates:

    • A. Dependent names are resolved during the first phase.
    • B. Non-dependent names are resolved at template definition.
    • C. ADL (Argument-Dependent Lookup) occurs in the second phase.
    • D. typename is required for dependent types in the first phase.
  3. Which scenarios violate the One-Definition Rule (ODR) for templates?

    • A. Two explicit specializations of std::vector<bool> in different translation units.
    • B. Identical implicit instantiations of a class template across translation units.
    • C. A function template defined in a header included in multiple files.
    • D. A partial specialization declared after its primary template.
  4. Select valid uses of template template parameters:

    • A. template<template<typename> class Container> class MyClass { ... };
    • B. template<typename T, template<T> class U> void f() { ... }
    • C. template<template<auto> class Container> struct X { ... };
    • D. template<template<typename...> class C> void g(C<int>) { ... }
  5. Which statements about variable templates are true?

    • A. They must be declared with constexpr.
    • B. They can be specialized like class templates.
    • C. template<typename T> T pi = T(3.1415926535); is valid.
    • D. They cannot have non-type template parameters.
  6. Regarding SFINAE (Substitution Failure Is Not An Error):

    • A. It applies to errors in the immediate context of substitution.
    • B. std::enable_if_t leverages SFINAE to enable/disable overloads.
    • C. A failed static_assert inside a template triggers SFINAE.
    • D. It works with partial specializations of class templates.
  7. Which of the following are valid fold expressions (C++17)?

    • A. (args + ...)
    • B. (0 + ... + args)
    • C. (std::cout << ... << args)
    • D. (... && args)
  8. Identify correct behaviors of class template argument deduction (CTAD):

    • A. Deduction guides override constructor-based deduction.
    • B. std::array{1, 2, 3} deduces to std::array<int, 3>.
    • C. Explicit template arguments disable CTAD.
    • D. Aggregate classes cannot use CTAD without guides.
  9. About auto and template type deduction:

    • A. auto x{1, 2}; deduces x as std::initializer_list<int>.
    • B. auto&& follows universal reference rules.
    • C. decltype(auto) preserves value categories.
    • D. auto in lambda parameters requires C++20.
  10. Which techniques ensure compile-time polymorphism?

    • A. CRTP (Curiously Recurring Template Pattern)
    • B. Virtual functions
    • C. Tag dispatch
    • D. if constexpr branches

Design Questions (Hard Difficulty)

  1. Design a constexpr-enabled Tuple class template supporting:

    • Variable number of elements of heterogeneous types.
    • Compile-time element access via get<Index>(tuple).
    • Structured binding support.
      Provide test cases for construction, access, and structured bindings.
  2. Implement a TypeList metaprogramming utility with:

    • Operations: Length, Get, Concat, Filter (remove types matching a trait).
    • SFINAE-based trait to check if a type is in the TypeList.
      Test with TypeList<int, float, char> filtering for arithmetic types.
  3. Create a ThreadSafeQueue class template using:

    • A lock-based design with std::mutex and std::condition_variable.
    • Perfect forwarding for emplace().
    • Timeout support for try_pop().
      Test with producer-consumer threads and verify exception safety.
  4. Design a Variant class mimicking std::variant with:

    • Type-safe storage for alternative types.
    • visit() using a generic lambda and overload pattern.
    • Compile-time checks for duplicate types.
      Test with Variant<int, float, std::string> and visitation.
  5. Implement a Serializer using SFINAE to handle:

    • Primitive types (direct serialization).
    • Containers with begin()/end().
    • Custom serialization via serialize() member function.
      Test with std::vector, a custom struct, and a std::map.

Answers and Explanations

Multiple-Choice Answers
  1. B, D

    • B: Non-deduced contexts (e.g., nested types) prevent deduction.
    • D: SFINAE discards invalid substitutions without errors.
    • A: Deduction ignores implicit conversions.
    • C: decltype(auto) deduces references for lvalues, values otherwise.
  2. B, C

    • B: Non-dependent names are resolved at definition.
    • C: ADL occurs during the second phase.
    • A: Dependent names are resolved at instantiation.
    • D: typename is needed in the second phase.
  3. A

    • A: Multiple explicit specializations violate ODR.
    • B/C: Implicit instantiations and inline definitions are allowed.
    • D: Partial specializations must follow primary templates.
  4. A, C, D

    • A/C/D: Valid syntax for template template parameters.
    • B: Non-type template parameters cannot be dependent on type T.
  5. B, C

    • B: Variable templates can be specialized.
    • C: Valid variable template.
    • A: constexpr is optional.
    • D: Non-type parameters are allowed.
  6. A, B

    • A/B: SFINAE applies to immediate context and enable_if.
    • C: static_assert causes hard errors.
    • D: SFINAE doesn’t apply to class partial specializations.
  7. A, D

    • A/D: Valid unary/binary fold expressions.
    • B: Invalid syntax (parentheses needed).
    • C: Binary folds require consistent operators.
  8. A, C

    • A/C: Guides override, explicit args disable CTAD.
    • B: std::array deduction requires std::to_array.
    • D: Aggregates can use CTAD with C++17.
  9. B, C, D

    • B/D: Correct universal ref and C++20 lambda syntax.
    • C: decltype(auto) preserves references.
    • A: auto x{1,2}; is invalid (C++17: single-element list).
  10. A, C, D

    • A/C/D: Compile-time techniques.
    • B: Runtime polymorphism.

Design Answers
  1. Tuple Implementation

    template<typename... Ts>
    class Tuple {};
    
    template<typename T, typename... Ts>
    class Tuple<T, Ts...> : Tuple<Ts...> {
        T value;
    public:
        Tuple(T t, Ts... ts) : Tuple<Ts...>(ts...), value(t) {}
        template<size_t I> auto& get() { /*...*/ }
    };
    // Test:
    Tuple<int, float> t(42, 3.14f);
    auto& x = t.get<0>(); // 42
    
  2. TypeList Metaprogramming

    template<typename... Ts> struct TypeList {};
    template<typename List> struct Length;
    template<template<class...> class List, typename... Ts>
    struct Length<List<Ts...>> : std::integral_constant<size_t, sizeof...(Ts)> {};
    
    // Test:
    using TL = TypeList<int, float, char>;
    static_assert(Length<TL>::value == 3);
    
  3. ThreadSafeQueue

    template<typename T>
    class ThreadSafeQueue {
        std::queue<T> queue;
        std::mutex mtx;
        std::condition_variable cv;
    public:
        template<typename U>
        void emplace(U&& u) {
            std::lock_guard lock(mtx);
            queue.emplace(std::forward<U>(u));
            cv.notify_one();
        }
    };
    // Test with threads...
    
  4. Variant Class

    template<typename... Ts>
    class Variant {
        std::aligned_union_t<0, Ts...> storage;
        size_t index;
    public:
        template<typename T> requires (std::is_constructible_v<T>)
        Variant(T&& t) { /*...*/ }
    };
    // Test visitation...
    
  5. Serializer with SFINAE

    template<typename T, typename = void>
    struct Serializer {
        static void serialize(const T& t) { /*...*/ }
    };
    // Test with custom types...
    

网站公告

今日签到

点亮在社区的每一天
去签到