Chapter 8: Advanced Template Metaprogramming in C++__《C++ Templates》notes

Advanced Template Metaprogramming in C++

      • 1. Key Concepts & Code Explanations
        • 1.1 SFINAE (Substitution Failure Is Not An Error)
        • 1.2 `constexpr` and Compile-Time Computation
        • 1.3 Type Traits
        • 1.4 Variadic Templates with Recursion
        • 1.5 C++20 Concepts
      • 2. Hard Multiple-Choice Questions (10)
        • Questions 1-5
        • Questions 6-10
      • Answers & Explanations
      • 3. Hard Design Questions (5)
        • Question 1: SFINAE-Based Type Trait
        • Question 2: Compile-Time Prime Check
        • Question 3: CRTP (Curiously Recurring Template Pattern)
        • Question 4: Concept for `std::hash` Support
        • Question 5: Variadic Template with Fold Expression
      • Summary


1. Key Concepts & Code Explanations

1.1 SFINAE (Substitution Failure Is Not An Error)

What: Enables/Disables template overloads based on type properties.
Example: Enable a function only for integer types.

#include 

template<typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
process(T val) {
    std::cout << "Processing integral: " << val << "\n";
}

template<typename T>
typename std::enable_if<!std::is_integral<T>::value, void>::type
process(T val) {
    std::cout << "Processing non-integral: " << val << "\n";
}

int main() {
    process(42);      // Output: Processing integral: 42
    process(3.14);    // Output: Processing non-integral: 3.14
}

1.2 constexpr and Compile-Time Computation

What: Evaluate expressions at compile time.
Example: Compile-time factorial.

constexpr int factorial(int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

int main() {
    constexpr int result = factorial(5);
    static_assert(result == 120, "Factorial of 5 must be 120");
    std::cout << result; // Output: 120
}

1.3 Type Traits

What: Inspect/modify type properties (e.g., std::is_pointer, std::remove_reference).
Example: Check if a type is a pointer.

template<typename T>
struct is_pointer {
    static constexpr bool value = false;
};

template<typename T>
struct is_pointer<T*> {
    static constexpr bool value = true;
};

int main() {
    std::cout << is_pointer<int*>::value;   // Output: 1 (true)
    std::cout << is_pointer<int>::value;    // Output: 0 (false)
}

1.4 Variadic Templates with Recursion

What: Process parameter packs recursively.
Example: Compile-time sum of a parameter pack.

template<typename T>
T sum(T val) { return val; }

template<typename T, typename... Args>
T sum(T first, Args... rest) {
    return first + sum(rest...);
}

int main() {
    std::cout << sum(1, 2, 3, 4); // Output: 10
}

1.5 C++20 Concepts

What: Constrain template parameters with readable syntax.
Example: Ensure a type supports the += operator.

#include 

template<typename T>
concept Addable = requires(T a, T b) {
    { a += b } -> std::same_as<T&>;
};

template<Addable T>
void add(T& a, const T& b) { a += b; }

int main() {
    int x = 5;
    add(x, 3);
    std::cout << x; // Output: 8
}

2. Hard Multiple-Choice Questions (10)

Questions 1-5

Q1: Which code uses SFINAE correctly to enable a function for arithmetic types?
A)

template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>  
void func(T val) {}  

B)

template<typename T>  
void func(T val) requires std::is_arithmetic_v<T> {}  

C) Both A and B
D) Neither

Q2: What is the output of sum(1, 2.5, 3) if sum uses auto return type?
A) 6.5
B) 6
C) Compile error
D) Undefined

Q3: Which type trait removes const from const int*?
A) std::remove_const
B) std::remove_pointer
C) std::decay
D) std::remove_reference

Q4: What is the result of std::is_same_v, int>?
A) true
B) false
C) Compile error
D) Undefined

Q5: What does requires std::integral enforce?
A) T is an integer type
B) T is an integral constant
C) T supports arithmetic operations
D) T is a template parameter


Questions 6-10

Q6: Which code checks if a type has a resize() member function?
A)

template<typename T>  
concept Resizable = requires(T t) { t.resize(0); };  

B)

template<typename T>  
struct has_resize {  
    template<typename U>  
    static auto test(U u) -> decltype(u.resize(0), void());  
    static bool value = std::is_invocable_v<test<T>, T>;  
};  

C) Both A and B
D) Neither

Q7: What is the output of:

constexpr int x = std::integral_constant<int, 5>::value;  
std::cout << x;  

A) 5
B) 0
C) Compile error
D) Undefined

Q8: Which code computes the factorial of 5 at compile time?
A)

constexpr int fact(int n) { return n <= 1 ? 1 : n * fact(n-1); }  
int main() { constexpr int x = fact(5); }  

B)

template<int N>  
struct Factorial { static constexpr int value = N * Factorial<N-1>::value; };  
template<> struct Factorial<0> { static constexpr int value = 1; };  
int main() { constexpr int x = Factorial<5>::value; }  

C) Both A and B
D) Neither

Q9: What does std::void_t do?
A) Always resolves to void
B) Causes substitution failure if T is invalid
C) Checks if T is a valid type
D) All of the above

Q10: Which code uses fold expressions correctly?
A)

template<typename... Args>  
auto sum(Args... args) { return (... + args); }  

B)

template<typename... Args>  
auto sum(Args... args) { return (args + ...); }  

C) Both A and B
D) Neither


Answers & Explanations

  1. C (Both use SFINAE/concepts).
  2. A (Auto deduces double).
  3. B (std::remove_pointer::typeconst int).
  4. A (remove_cv_t removes const and volatile).
  5. A (Enforces T is integral).
  6. C (Both check resize() existence).
  7. A (integral_constant stores 5).
  8. C (Both compute factorial at compile time).
  9. D (std::void_t is used in SFINAE to check validity).
  10. C (Both are valid fold expressions).

3. Hard Design Questions (5)

Question 1: SFINAE-Based Type Trait

Task: Create a trait is_container that checks if a type has begin() and end().

#include 

template<typename T, typename = void>
struct is_container : std::false_type {};

template<typename T>
struct is_container<T, std::void_t<
    decltype(std::declval<T>().begin()),
    decltype(std::declval<T>().end())
>> : std::true_type {};

int main() {
    std::cout << is_container<std::vector<int>>::value; // Output: 1
    std::cout << is_container<int>::value;              // Output: 0
}

Question 2: Compile-Time Prime Check

Task: Use template metaprogramming to check if a number is prime.

template<int N, int D = N-1>
struct is_prime {
    static constexpr bool value = (N % D != 0) && is_prime<N, D-1>::value;
};

template<int N>
struct is_prime<N, 1> { static constexpr bool value = true; };

template<int N>
struct is_prime<N, 0> { static constexpr bool value = false; };

int main() {
    static_assert(!is_prime<4>::value, "4 is not prime");
    static_assert(is_prime<5>::value, "5 is prime");
}

Question 3: CRTP (Curiously Recurring Template Pattern)

Task: Implement a Cloneable base class using CRTP.

template<typename Derived>
class Cloneable {
public:
    Derived* clone() const {
        return new Derived(static_cast<const Derived&>(*this));
    }
};

class Widget : public Cloneable<Widget> {
public:
    Widget() = default;
    Widget(const Widget&) { std::cout << "Widget cloned\n"; }
};

int main() {
    Widget w;
    auto* w2 = w.clone(); // Output: Widget cloned
}

Question 4: Concept for std::hash Support

Task: Use C++20 concepts to ensure a type is hashable.

#include 
#include 

template<typename T>
concept Hashable = requires(T t) {
    { std::hash<T>{}(t) } -> std::convertible_to<std::size_t>;
};

template<Hashable T>
void hash_check(T val) {
    std::cout << std::hash<T>{}(val) << "\n";
}

int main() {
    hash_check(42); // OK: int is hashable
    // hash_check(std::vector()); // Error: vector is not hashable
}

Question 5: Variadic Template with Fold Expression

Task: Implement a product function using fold expressions.

template<typename... Args>
auto product(Args... args) {
    return (... * args);
}

int main() {
    std::cout << product(2, 3, 4); // Output: 24
    std::cout << product(1.5, 2.0); // Output: 3
}

Summary

This guide covers SFINAE, constexpr, type traits, variadic templates, and C++20 concepts. The exercises validate advanced template metaprogramming techniques, including compile-time computations, CRTP, and custom type traits. Test cases ensure correctness for edge cases like prime checks and hashability.

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