• C++模板元模板(异类词典与policy模板)- - - 题目答案


    目录

    一、书中第一题

    二、书中第三题

    三、书中第五题

    四、书中第六题

    五、书中第七题

    六、书中十一题

    七、书中十二题

    八、 书中十三题

    总结


    一、书中第一题

    1. #include
    2. template <typename T, size_t N>
    3. struct NSVarTypeDict {
    4. static void Create_() {
    5. CreateHelper();
    6. }
    7. private:
    8. template <size_t M, typename U>
    9. struct CreateHelper {
    10. static void Apply() {
    11. // 构造元素的逻辑
    12. std::cout << "Constructing element at index " << M << std::endl;
    13. CreateHelper1, U>::Apply();
    14. CreateHelper1, U>::Apply();
    15. }
    16. };
    17. template <typename U>
    18. struct CreateHelper<0, U> {
    19. static void Apply() {
    20. // 基础情况:当索引为0时停止递归
    21. }
    22. };
    23. };
    24. int main() {
    25. NSVarTypeDict<int, 8>::Create_();
    26. return 0;
    27. }

    二、书中第三题

    1. #include
    2. #include
    3. template <size_t N, size_t M, typename T1, typename T2>
    4. struct NSVarTypeDict {
    5. using NewTupleType_ = std::conditional_t;
    6. };
    7. int main() {
    8. struct Type1 {
    9. int value;
    10. };
    11. struct Type2 {
    12. double value;
    13. };
    14. typename NSVarTypeDict<3, 4, Type1, Type2>::NewTupleType_ var1; // 选择Type2
    15. typename NSVarTypeDict<5, 5, Type1, Type2>::NewTupleType_ var2; // 选择Type1
    16. return 0;
    17. }
    • 代码结构更清晰:通过使用std::conditional_t将特化1和特化2合并成一个表达式,避免了重复的代码逻辑和声明。
    • 可维护性提高:合并后的代码更加简洁,易于阅读和理解。

    在使用std::conditional_t进行合并后,简化的代码更易于理解和维护,并且避免了重复代码的问题,因此整体上更具优势。

    三、书中第五题

    1. #include
    2. class VarTypeDict {
    3. public:
    4. template<typename T>
    5. void Set(T&& value) && {
    6. // 只能用于右值的Set函数实现
    7. std::cout << "Setting value for rvalue: " << value << std::endl;
    8. }
    9. template<typename T>
    10. void Set(T& value) & {
    11. // 能用于左值的Set函数实现
    12. std::cout << "Setting value for lvalue: " << value << std::endl;
    13. }
    14. };
    15. int main() {
    16. VarTypeDict::Values values;
    17. int x = 10;
    18. values.Set(x); // 调用能用于左值的Set函数
    19. values.Set(20); // 调用只能用于右值的Set函数
    20. return 0;
    21. }

    优势和劣势:

    优势:

    • 能用于左值的函数可以接受左值类型的参数,提供更灵活的使用方式。
    • 通过重载的方式,程序可以根据传入值的类型来选择合适的函数重载,提高了程序的通用性和适用性。

    劣势:

    • 在某些情况下,程序员需要知道传入的值是左值还是右值,否则会导致调用不符合预期的函数。

    总体上,能用于左值的函数相比只能用于右值的函数更加灵活,可以接受更多种类的参数,但需要谨慎使用以避免潜在的问题。

    四、书中第六题

    构造函数的访问权限从public修改为private或protected是一种有效的封装手段,可以限制其它类对构造函数的直接访问,从而更好地控制类的实例化。

    理由:

    1. 控制类的实例化:通过将构造函数的访问权限设为private或protected,可以防止该类被直接实例化,从而强制使用特定的接口或方法来获取该类的实例。
    2. 封装实现细节:将构造函数的访问权限限制在类内部,有助于隐藏实现细节,提高类的封装性和安全性。
    3. 限制子类的实例化:将构造函数的访问权限设为protected可以使子类能够调用构造函数,但限制外部代码直接实例化子类。

    接下来,让我们尝试将Values的构造函数访问权限从public修改为private,然后编译检查是否符合预期。

    1. class VarTypeDict {
    2. public:
    3. class Values {
    4. private: // 修改构造函数的访问权限为private
    5. Values() {}
    6. friend class VarTypeDict; // 允许VarTypeDict类访问构造函数
    7. public:
    8. template<typename T>
    9. void Set(T&& value) && {
    10. std::cout << "Setting value for rvalue: " << value << std::endl;
    11. }
    12. template<typename T>
    13. void Set(T& value) & {
    14. std::cout << "Setting value for lvalue: " << value << std::endl;
    15. }
    16. };
    17. };

    根据上述代码修改,我们将Values的构造函数的访问权限修改为private,并使用friend关键字允许VarTypeDict类访问构造函数。

    在实际编译时,会发现尝试在外部进行Values类的实例化会导致编译错误,而在VarTypeDict类中可以正常调用构造函数来创建Values的实例,符合我们的预期。

    这种修改符合我们的预期,成功限制了外部代码对Values类的直接实例化,增强了类的封装性和安全性。

    五、书中第七题

    1. #include
    2. #include
    3. class VarTypeDict {
    4. public:
    5. class Values {
    6. private:
    7. std::tuple<int, double, std::string> data; // 使用std::tuple替换指针数组
    8. public:
    9. template<typename T>
    10. void Set(T&& value) && {
    11. std::get(data) = value; // 使用std::get获取元素
    12. }
    13. template<typename T>
    14. T Get() {
    15. return std::get(data); // 使用std::get获取元素
    16. }
    17. };
    18. };

    示例中,使用std::tuple替换了指针数组,这样就不需要显式进行内存分配和释放。通过std::get函数,我们可以在不了解具体元素类型的情况下,访问和操作特定类型的元素。

    新版本的复杂度分析:

    1. 时间复杂度:使用std::tuple并不会增加操作的时间复杂度,因为std::tuple内部通常是通过继承和模板特化来实现的,因此元素访问的操作也是常数时间复杂度。
    2. 空间复杂度:使用std::tuple可能会略微增加一些内存消耗,因为std::tuple通常会引入一些辅助结构来实现元素访问的功能。

    总体而言,通过使用std::tuple替换指针数组,新版本的VarTypeDict复杂度并未明显增加,并且更加安全和易用,因为不再需要显式操作内存分配与释放,同时使用std::tuple提供了更好的类型安全性和代码可读性。

    六、书中十一题

    1. #include
    2. // 定义策略类模板
    3. template <typename T>
    4. class Policy {
    5. public:
    6. void DoSomething() {
    7. std::cout << "Doing something with type " << typeid(T).name() << std::endl;
    8. }
    9. };
    10. // 定义宏来简化策略对象的定义
    11. #define POLICY_POLICY(PolicyType, ValueType) Policy>
    12. int main() {
    13. // 构造模板策略对象
    14. POLICY_POLICY(std::vector, int) policyObject;
    15. // 使用策略对象
    16. policyObject.DoSomething();
    17. return 0;
    18. }

    七、书中十二题

    1. #include
    2. #include
    3. #include
    4. class NamedParameters {
    5. public:
    6. class Values {
    7. private:
    8. int value;
    9. public:
    10. Values(int val) : value(val) {}
    11. template<typename T>
    12. T Get() const {
    13. return T(value); // 复制构造返回值
    14. }
    15. template<typename T>
    16. typename std::remove_const::type&& Get() && {
    17. return std::move(value); // 返回右值引用,使用移动语义
    18. }
    19. };
    20. };
    21. int main() {
    22. NamedParameters::Values values(123);
    23. // 使用左值引用调用Get函数
    24. int copyValue = values.Get<int>();
    25. std::cout << "Copy Value: " << copyValue << std::endl;
    26. // 使用右值引用调用Get函数
    27. int&& moveValue = std::move(values).Get<int>();
    28. std::cout << "Move Value: " << moveValue << std::endl;
    29. return 0;
    30. }

    示例中,我们在NamedParameters::Values类中引入了一个新的Get函数模板,并使用std::remove_const来移除返回类型的const限定符。在Get函数的右值引用版本中,我们使用std::move对底层数据对象进行移动,通过移动语义返回右值引用。这样,对于右值引用的情况,我们可以通过移动来返回底层数据对象,减少了额外的复制成本。

    在主函数中,我们首先使用左值引用调用Get函数,对底层数据对象进行复制。然后,我们使用std::move将values对象转换为右值引用,并调用Get函数获取右值引用的底层数据对象。通过输出的结果,我们可以看到使用移动语义获取的值。

    通过引入移动语义的Get函数,我们可以根据NamedParameters::Values对象的左值或右值特性,使用不同的方式返回底层数据对象,从而减少复制带来的额外消耗。

    八、 书中十三题

    1. #include
    2. #include
    3. // 定义异类词典模板
    4. template<typename... Ts>
    5. struct VarTypeDict {};
    6. // 添加元素的元函数 AddItem
    7. template <typename Dict, typename NewItem>
    8. struct AddItem;
    9. // 特化:向空的异类词典添加元素
    10. template <typename NewItem>
    11. struct AddItem, NewItem> {
    12. using type = VarTypeDict;
    13. };
    14. // 特化:向非空的异类词典添加元素
    15. template <typename Head, typename... Tail, typename NewItem>
    16. struct AddItem, NewItem> {
    17. using type = typename std::conditional::value,
    18. VarTypeDict,
    19. typename AddItem, NewItem>::type>::type;
    20. };
    21. // 删除元素的元函数 DelItem
    22. template <typename Dict, typename ItemToDelete>
    23. struct DelItem;
    24. // 特化:从空的异类词典删除元素,报错
    25. template <typename ItemToDelete>
    26. struct DelItem, ItemToDelete> {
    27. static_assert(sizeof(ItemToDelete) == 0, "Item does not exist in VarTypeDict");
    28. };
    29. // 特化:从包含元素的异类词典删除元素
    30. template <typename Head, typename... Tail, typename ItemToDelete>
    31. struct DelItem, ItemToDelete> {
    32. using type = typename std::conditional::value,
    33. VarTypeDict,
    34. typename AddItem<typename DelItem, ItemToDelete>::type, Head>::type>::type;
    35. };
    36. int main() {
    37. // 使用示例:添加元素
    38. using MyDict = VarTypeDict<struct A, struct B>;
    39. using DictWithMoreItems = typename AddItemstruct C>::type;
    40. using DictWithLessItems = typename DelItem::type;
    41. // 使用示例:删除不存在的元素,会报错
    42. // using DictWithInvalidDel = typename DelItem::type; // 会产生编译时错误
    43. std::cout << typeid(DictWithMoreItems).name() << std::endl; // 输出 VarTypeDict
    44. std::cout << typeid(DictWithLessItems).name() << std::endl; // 输出 VarTypeDict
    45. return 0;
    46. }

    示例中,我们定义了AddItem和DelItem两个元函数来实现添加和删除元素。通过模板特化和递归调用,我们可以实现对异类词典的元素操作。在DelItem的特化中,我们使用静态断言来检测是否要删除的元素存在于异类词典中,以便在编译时发现错误。

    在主函数中,我们使用示例展示了AddItem与DelItem元函数的使用方式,并输出了修改后的异类词典类型。同时,我们还展示了尝试删除不存在的元素时会触发编译时错误的情况。

    通过这种方式,我们可以在编译期间操作异类词典的元素,动态地修改异类词典的类型。


    总结

    下一章开始学习深度学习简介!!!

  • 相关阅读:
    成本翻倍,部署复杂!为什么要停止使用Kubernetes
    易基因|ChIP-seq等实验揭示CHD6转录激活前列腺癌通路的关键功能 | 肿瘤耐药研究
    2023年十大地推拉新接单平台和网推接单平台,都是一手单
    怎么截取视频做gif动图?手把手教你视频在线转gif制作
    代码随想录算法训练营day34
    SwiftUI 6.0(Xcode 16)全新 @Entry 和 @Previewable 宏让开发妙趣横生
    摄影后期图像编辑软件Lightroom Classic 2023 mac中文特点介绍
    LeetCode第232题—用栈实现队列
    上海华清远见
    偏向锁,轻量级锁,重量级锁的核心原理
  • 原文地址:https://blog.csdn.net/cylddrmm123/article/details/134365318