Chapter 24: Typelists_《C++ Templates》notes

Typelists

        • 1. Anatomy of a Typelist
        • 2. Accessing Elements
        • 3. Appending Types
        • 4. Reversing a Typelist
        • 5. Length of Typelist
        • 6. Compile-Time Testing with `main`
      • Multiple-Choice Questions
      • Detailed Design Questions
      • Answers & Explanations
        • Multiple-Choice Answers
        • Design Answers
      • Key Takeaways


1. Anatomy of a Typelist

A typelist is a compile-time structure holding a sequence of types. It is implemented via recursive template specialization.

Code: Basic Typelist Definition

template<typename... Elements>
struct Typelist {};

Test Case

using MyList = Typelist<int, double, char>;
// No runtime output; validity is checked at compile time.

2. Accessing Elements

a. Front: Get the first type.
Code

template<typename List>
struct Front;

template<typename Head, typename... Tail>
struct Front<Typelist<Head, Tail...>> {
    using type = Head;
};

Test Case

using MyList = Typelist<int, double, char>;
static_assert(std::is_same_v<Front<MyList>::type, int>, "Front failed");

b. PopFront: Remove the first type.
Code

template<typename List>
struct PopFront;

template<typename Head, typename... Tail>
struct PopFront<Typelist<Head, Tail...>> {
    using type = Typelist<Tail...>;
};

Test Case

using MyList = Typelist<int, double, char>;
using AfterPop = PopFront<MyList>::type;
static_assert(std::is_same_v<AfterPop, Typelist<double, char>>, "PopFront failed");

3. Appending Types

Append a type to the typelist.

Code

template<typename List, typename NewElement>
struct Append;

template<typename... Elements, typename NewElement>
struct Append<Typelist<Elements...>, NewElement> {
    using type = Typelist<Elements..., NewElement>;
};

Test Case

using Original = Typelist<int, double>;
using Appended = Append<Original, char>::type;
static_assert(std::is_same_v<Appended, Typelist<int, double, char>>, "Append failed");

4. Reversing a Typelist

Reverse the order of types using recursion.

Code

template<typename List>
struct Reverse;

template<typename Head, typename... Tail>
struct Reverse<Typelist<Head, Tail...>> {
    using type = typename Append<
        typename Reverse<Typelist<Tail...>>::type,
        Typelist<Head>
    >::type;
};

template<>
struct Reverse<Typelist<>> {
    using type = Typelist<>;
};

Test Case

using MyList = Typelist<int, double, char>;
using Reversed = Reverse<MyList>::type;
static_assert(std::is_same_v<Reversed, Typelist<char, double, int>>, "Reverse failed");

5. Length of Typelist

Calculate the number of types.

Code

template<typename List>
struct Length;

template<typename... Elements>
struct Length<Typelist<Elements...>> {
    static constexpr std::size_t value = sizeof...(Elements);
};

Test Case

using MyList = Typelist<int, double, char>;
static_assert(Length<MyList>::value == 3, "Length failed");

6. Compile-Time Testing with main

Use static_assert to validate typelist operations at compile time.

Code

#include 
#include 

int main() {
    // Tests are compile-time; no runtime output.
    std::cout << "All tests passed at compile time!\n";
    return 0;
}

Multiple-Choice Questions

  1. Which of the following correctly describe the structure of a Typelist?

    • A) A runtime-linked list of type pointers
    • B) A recursive template structure with a head and tail
    • C) A class template with variadic parameters
    • D) A tuple-like structure with fixed-size type storage
  2. What is the time complexity of accessing the Nth element in a Typelist using linear recursion?

    • A) O(1)
    • B) O(N)
    • C) O(log N)
    • D) O(N^2)
  3. Which techniques are valid for reversing a Typelist?

    • A) Using recursive template specialization
    • B) Using constexpr functions at runtime
    • C) Leveraging pack expansion with index sequences
    • D) Storing types in a temporary tuple
  4. What is required to implement a “best match” algorithm for Typelists?

    • A) Partial template specialization
    • B) SFINAE (Substitution Failure Is Not An Error)
    • C) Runtime type comparison
    • D) Template template parameters
  5. Which are valid applications of Typelists?

    • A) Implementing compile-time state machines
    • B) Generating dispatch tables for variant types
    • C) Runtime polymorphism
    • D) Serializing objects to JSON
  6. How does inserting a type into a Typelist work?

    • A) Requires O(N) template instantiations
    • B) Uses std::conditional for branch selection
    • C) Relies on if constexpr for recursion
    • D) Mutates the original Typelist
  7. What is the purpose of IndexList/IndexSequence in Typelist algorithms?

    • A) To enable pack expansion optimizations
    • B) To store runtime indices for type access
    • C) To generate compile-time integer sequences
    • D) To validate type boundaries
  8. Which operations are possible with nontype Typelists?

    • A) Sorting integers at compile time
    • B) Storing floating-point values
    • C) Deduction of nontype template parameters
    • D) Runtime type introspection
  9. What is a key advantage of using pack expansions over recursive template instantiations?

    • A) Reduced compile-time memory usage
    • B) Support for runtime polymorphism
    • C) Elimination of template recursion depth limits
    • D) Simpler syntax
  10. Which C++ features are essential for Typelist implementations?

    • A) Variadic templates
    • B) constexpr functions
    • C) RTTI (Runtime Type Information)
    • D) Template partial specialization

Detailed Design Questions

  1. Implement a Reverse algorithm for Typelists using both recursive and pack-expansion approaches. Compare their compile-time performance.

    // Recursive approach
    template<typename TList> struct Reverse;
    template<> struct Reverse<NullType> { using type = NullType; };
    template<typename Head, typename Tail>
    struct Reverse<Typelist<Head, Tail>> {
        using type = Append<typename Reverse<Tail>::type, Head>;
    };
    
    // Pack-expansion approach
    template<typename... Ts> 
    struct Reverse<std::tuple<Ts...>> {
        using type = std::tuple<typename Reverse<std::tuple<Ts...>>::type...>;
    };
    
  2. Design a InsertionSort algorithm for a Typelist of integers, sorting them at compile time.

    template<typename TList> struct InsertionSort;
    template<> struct InsertionSort<NullType> { using type = NullType; };
    template<typename Head, typename Tail>
    struct InsertionSort<Typelist<Head, Tail>> {
        using sortedTail = typename InsertionSort<Tail>::type;
        using type = InsertSorted<sortedTail, Head>;
    };
    template<typename T, typename U, typename... Ts>
    struct InsertSorted<Typelist<U, Ts...>, T> {
        using type = std::conditional_t<(T < U), Typelist<T, U, Ts...>, Typelist<U, typename InsertSorted<Typelist<Ts...>, T>::type>>;
    };
    
  3. Create a Transform algorithm that converts a Typelist of types into a Typelist of their sizes (e.g., sizeof(int)size_t).

    template<template<typename> class F, typename TList> struct Transform;
    template<template<typename> class F>
    struct Transform<F, NullType> { using type = NullType; };
    template<template<typename> class F, typename Head, typename Tail>
    struct Transform<F, Typelist<Head, Tail>> {
        using type = Typelist<F<Head>, typename Transform<F, Tail>::type>;
    };
    
  4. Implement a Filter algorithm that removes all types from a Typelist where std::is_integral_v is false.

    template<template<typename> class Pred, typename TList> struct Filter;
    template<template<typename> class Pred>
    struct Filter<Pred, NullType> { using type = NullType; };
    template<template<typename> class Pred, typename Head, typename Tail>
    struct Filter<Pred, Typelist<Head, Tail>> {
        using type = std::conditional_t<Pred<Head>::value,
            Typelist<Head, typename Filter<Pred, Tail>::type>,
            typename Filter<Pred, Tail>::type>;
    };
    
  5. Design a Cons-style Typelist supporting O(1) head/tail access and benchmark it against the standard recursive approach.

    template<typename... Ts> struct ConsTypelist;
    template<typename Head, typename... Tail>
    struct ConsTypelist<Head, Tail...> {
        using head = Head;
        using tail = ConsTypelist<Tail...>;
    };
    template<> struct ConsTypelist<> { /* Base case */ };
    

Answers & Explanations

Multiple-Choice Answers
  1. B, C
    Typelists are recursive template structures (B) and use variadic parameters ©.
    A/D: Runtime structures and fixed-size storage are incorrect.

  2. B
    Linear recursion results in O(N) complexity for Nth element access.

  3. A, C
    Recursive specialization (A) and pack expansions with index sequences © are valid.
    B: constexpr works at compile time but isn’t used here. D: Tuples aren’t part of Typelist reversal.

  4. A, B
    Partial specialization (A) and SFINAE (B) are needed for compile-time type matching.

  5. A, B
    Typelists enable compile-time state machines (A) and variant dispatch tables (B).
    C/D: Runtime features are unrelated.

  6. A, B
    Insertion requires O(N) instantiations (A) and std::conditional (B).
    C: if constexpr isn’t used here. D: Typelists are immutable.

  7. A, C
    IndexLists enable pack expansion optimizations (A) and compile-time sequences ©.
    B/D: Runtime indices and validation are irrelevant.

  8. A, C
    Nontype Typelists sort integers (A) and deduce parameters ©.
    B: Floating points are invalid. D: No runtime introspection.

  9. A, C
    Pack expansions reduce memory (A) and avoid recursion limits ©.
    B/D: Irrelevant to compile-time optimizations.

  10. A, D
    Variadic templates (A) and partial specialization (D) are essential.
    B: constexpr isn’t required. C: RTTI is runtime-only.


Design Answers
  1. Reverse Implementation

    // Test case
    using MyList = Typelist<int, char, double>;
    using Reversed = Reverse<MyList>::type; // Typelist
    static_assert(std::is_same_v<Get<0, Reversed>, double>);
    
  2. InsertionSort

    using IntList = Typelist<3, 1, 4, 2>;
    using Sorted = InsertionSort<IntList>::type; // Typelist<1, 2, 3, 4>
    static_assert(Get<0, Sorted>::value == 1);
    
  3. Transform

    template<typename T> struct SizeOf { static constexpr size_t value = sizeof(T); };
    using Sizes = Transform<SizeOf, Typelist<int, double>>::type; // Typelist<4, 8>
    
  4. Filter

    using Filtered = Filter<std::is_integral, Typelist<int, char, double>>::type; // Typelist
    
  5. ConsTypelist Benchmark

    using ConsList = ConsTypelist<int, char, double>;
    static_assert(std::is_same_v<typename ConsList::head, int>);
    

Key Takeaways

  • Typelists enable compile-time type manipulation.
  • Recursive templates are central to operations like Reverse.
  • static_assert validates typelist logic during compilation.
  • Each operation (e.g., Front, Append) is a template metafunction returning a type or value.

This approach leverages C++'s type system to enforce correctness without runtime overhead.

你可能感兴趣的:(c/c++,c++,笔记,开发语言)