当前位置: 首页 > news >正文

C++模板元编程:编译时的魔法

1. 引言

在C++的世界中,模板元编程是一种在编译时执行计算的强大技术。它允许开发者编写高度灵活和高效的代码,这些代码可以在不牺牲性能的前提下,根据类型和值的不同而变化。本文将深入探讨模板元编程的奥秘,并展示如何在现代C++开发中利用这一技术。

2. C++模板基础

C++模板是泛型编程的基石,它们提供了一种编写与数据类型无关的代码的方法。在这一节中,我们将深入探索模板的基本概念、使用方式以及它们在C++中的多种应用。

2.1 模板的定义和使用

模板允许我们定义可以处理多种数据类型的函数或类。下面是一个简单的函数模板示例,它演示了如何打印任意类型的数据:

#include <iostream>
#include <string>template <typename T>
void printValue(T value) {std::cout << value << std::endl;
}int main() {printValue(10);        // 打印整数printValue(3.14);      // 打印浮点数printValue("Hello");   // 打印字符串return 0;
}

2.2 函数模板和类模板的区别

函数模板定义了操作数据的函数,而类模板定义了可以包含数据和行为的类型。以下是类模板的一个示例:

template <typename T>
class Stack {
private:T* elements;size_t size;size_t capacity;public:Stack(size_t initialCapacity);~Stack();void push(const T& element);T pop();size_t getSize() const;
};

这个Stack类模板可以用于创建任何类型的栈。

2.3 模板参数和模板特化

模板参数允许我们定义通用的代码,而模板特化则允许我们为特定的类型提供特定的实现。以下是模板特化的一个例子:

// 通用的print函数模板
template <typename T>
void print(T value) {std::cout << value << std::endl;
}// 特化版本,用于打印字符串
template <>
void print(const std::string& value) {std::cout << "'" << value << "'" << std::endl;
}int main() {print(123);    // 使用通用版本print("test"); // 使用特化版本return 0;
}

2.4 模板参数的默认值

我们可以为模板参数提供默认值,这样在调用模板时可以省略某些参数:

template <typename T, typename Allocator = std::allocator<T>>
class Vector {// ...
};

在这个例子中,如果用户没有指定分配器类型,Vector类模板将默认使用std::allocator

2.5 非类型模板参数

除了类型参数外,模板还可以接受非类型参数,如整数:

template <int size>
class FixedSizeArray {T (&data)[size];
public:FixedSizeArray(T* array) {for (int i = 0; i < size; ++i) {data[i] = array[i];}}
};

这个FixedSizeArray类模板创建了一个固定大小的数组的引用。

2.6 模板的友元声明

模板可以声明友元函数或类,这些友元可以访问模板的私有成员:

template <typename T>
class MyClass {
private:T privateData;public:template <typename U>friend class MyOtherClass;
};template <typename T>
class MyOtherClass {
public:void accessPrivateData(MyClass<T>& obj) {// 可以访问obj.privateData}
};

2.7 模板与继承

模板可以与继承一起使用,创建灵活的类型层次结构:

template <typename T>
class Base {virtual void doSomething() = 0;
};template <typename T>
class Derived : public Base<T> {void doSomething() override {// 实现细节}
};

在这个例子中,Derived类模板继承自Base类模板,并提供了doSomething的实现。

3. 模板元编程的基本原理

模板元编程是C++中一种高级技术,它允许开发者在编译时进行类型检查和计算。这种技术可以极大地提高程序的性能和灵活性。在本节中,我们将深入探讨模板元编程的基本原理,并提供丰富的示例来展示其应用。

3.1 编译时计算

模板元编程的核心是编译时计算。这意味着所有与类型相关的逻辑和决策都在编译阶段完成,从而避免了运行时的开销。以下是一个简单的示例,展示如何在编译时计算两个类型的平均大小:

template <typename T1, typename T2>
struct AverageSize {static const std::size_t value = (sizeof(T1) + sizeof(T2)) / 2;
};int main() {std::cout << AverageSize<int, double>::value << std::endl;// 输出 int 和 double 平均大小的字节数return 0;
}

3.2 模板元编程与运行时计算的区别

模板元编程与运行时计算的主要区别在于执行时机和性能。运行时计算在程序执行时进行,而模板元编程在编译时完成。这使得模板元编程可以提供更高的性能,因为它避免了运行时的类型检查和决策。

3.3 元组和变参模板的使用

元组和变参模板是模板元编程中两个重要的概念。元组允许我们以类型安全的方式存储和操作一系列不同类型的数据,而变参模板则允许我们编写接受任意数量参数的模板。

3.3.1 元组的使用

C++17引入了std::tuple,它是一个可以存储不同类型的固定大小序列。以下是一个使用元组的示例:

#include <tuple>template <typename... Types>
std::tuple<Types...> make_tuple(Types... args) {return std::make_tuple(args...);
}int main() {auto myTuple = make_tuple(1, 'a', 3.14);// myTuple 是一个包含 int, char 和 double 的 tuplereturn 0;
}
3.3.2 变参模板的使用

变参模板允许我们定义接受任意数量参数的模板。以下是一个变长参数求和的示例:

template <typename T>
T sum(T t) {return t;
}template <typename T, typename... Types>
T sum(T first, Types... rest) {return first + sum(rest...);
}int main() {std::cout << sum(1, 2, 3, 4) << std::endl;// 输出 10return 0;
}

3.4 递归模板模式

递归模板模式是模板元编程中的一个强大工具,它允许我们通过递归的方式进行类型和值的操作。以下是一个使用递归模板模式实现的阶乘计算:

template <int N>
struct Factorial {static const int value = N * Factorial<N - 1>::value;
};template <>
struct Factorial<0> {static const int value = 1;
};int main() {std::cout << Factorial<5>::value << std::endl;// 输出 120return 0;
}

3.5 模板元编程中的类型特性

类型特性(Type Traits)是模板元编程中用于类型属性查询的一种技术。例如,我们可以检查一个类型是否是指针类型:

template <typename T>
struct is_pointer : std::false_type {};template <typename T>
struct is_pointer<T*> : std::true_type {};int main() {std::cout << is_pointer<int>::value << std::endl; // 输出 0std::cout << is_pointer<int*>::value << std::endl; // 输出 1return 0;
}

3.6 模板元编程的限制

尽管模板元编程非常强大,但它也有一些限制,比如编译时间的增加和模板深度限制。以下是一个示例,展示如何处理模板深度限制:

template <typename T, T v>
struct integral_constant {static const T value = v;typedef T value_type;typedef integral_constant<T, v> type;
};typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;// 使用递归展开减少模板深度
template <bool...> struct bool_pack;template <bool... values>
struct all_true : true_type {};template <bool first, bool... rest>
struct all_true<first, rest...>: integral_constant<bool, first && all_true<rest...>::value> {};int main() {std::cout << all_true<true, true, true>::value << std::endl; // 输出 1std::cout << all_true<true, false, true>::value << std::endl; // 输出 0return 0;
}

4. 深入模板元编程

模板元编程不仅仅是在编译时进行计算,它还涉及到一系列高级技术,这些技术可以让我们编写出更加强大和灵活的代码。本节将深入探讨一些高级的模板元编程技巧,并提供丰富的示例来展示它们的应用。

4.1 高级模板技巧

4.1.1 SFINAE(Substitution Failure Is Not An Error)

SFINAE是一个强大的模板编程技术,它允许我们根据类型的特性来启用或禁用某些模板实例化。以下是一个使用SFINAE的示例:

#include <type_traits>template <typename T>
using add_const_t = typename std::add_const<T>::type;template <typename T>
auto func(T& t) -> add_const_t<decltype(t++)> {return t++;
}int main() {int i = 0;static_assert(std::is_same<decltype(func(i)), int&>::value, "Should be int&");return 0;
}
4.1.2 模板特化和偏特化

模板特化和偏特化允许我们为特定的类型或类型组合提供定制化的实现:

template <typename T1, typename T2>
struct Pair { /* ... */ };template <typename T>
struct Pair<T, T> {// 为相同类型的Pair提供特化实现
};template <typename T1>
struct Pair<T1, int> {// 为第二个类型为int的Pair提供偏特化实现
};

4.2 模板递归和模板展开

4.2.1 模板递归

模板递归是一种在模板定义中使用自身的方式,它可以用于实现递归算法:

template <int N>
struct Factorial {static constexpr int value = N * Factorial<N - 1>::value;
};template <>
struct Factorial<0> {static constexpr int value = 1;
};
4.2.2 模板展开

模板展开允许我们在编译时展开模板实例,这可以用于优化性能:

template <int... Ints>
struct IntSequence {};template <int N, int... Ints>
struct MakeIntSequence {using type = typename MakeIntSequence<N - 1, N - 1, Ints...>::type;
};template <int... Ints>
struct MakeIntSequence<0, Ints...> {using type = IntSequence<Ints...>;
};template <int N>
using MakeIntSequence_t = typename MakeIntSequence<N>::type;

4.3 模板元编程在算法实现中的应用

4.3.1 编译时排序

使用模板元编程,我们可以在编译时对数据进行排序,从而避免运行时的开销:

template <int... Ints>
struct SortedInts;template <int First, int Second, int... Rest>
struct SortedInts<First, Second, Rest...> {using type = typename std::conditional<First < Second,typename SortedInts<First, Rest...>::type,typename SortedInts<Second, First, Rest...>::type>::type;
};template <int Int>
struct SortedInts<Int> {using type = IntSequence<Int>;
};
4.3.2 编译时查找

模板元编程也可以用于在编译时执行查找操作:

template <int N, int... Ints>
struct FindIndex;template <int N, int Head, int... Tail>
struct FindIndex<N, N, Head, Tail...> {static const int value = 1;
};template <int N, int Head, int... Tail>
struct FindIndex<N, Head, Tail...> {static const int value = 1 + FindIndex<N, Tail...>::value;
};template <int N>
struct FindIndex<N> {static const int value = -1;
};

4.4 模板元编程的高级应用

4.4.1 元组算法

使用模板元编程,我们可以对元组中的元素执行算法,如应用函数或变换:

template <typename F, typename Tuple, std::size_t... Is>
void apply_impl(F&& f, Tuple&& t, std::index_sequence<Is...>) {using swallow = int[];(void)swallow{0, (void(f(std::get<Is>(std::forward<Tuple>(t)))), 0)...};
}template <typename F, typename Tuple>
void apply(F&& f, Tuple&& t) {apply_impl(std::forward<F>(f), std::forward<Tuple>(t),std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tuple>>::value>{});
}
4.4.2 表达式模板

表达式模板是一种使用模板元编程来延迟计算的技术,它常用于库设计中以提高性能:

template <typename T>
class Vector {// ...
public:template <typename U>Vector(const Vector<U>& other) {// ...}Vector operator+(const Vector& other) const {return Vector(*this).add(other);}private:Vector& add(const Vector& other) {// 实现向量加法return *this;}
};

5. 模板元编程的实用技巧

在深入探索模板元编程之后,我们需要了解一些实用的技巧来提高我们的编程实践。这些技巧将帮助我们编写更清晰、更高效且易于维护的模板代码。

5.1 编写可读性强的模板代码

5.1.1 清晰的模板参数命名

使用描述性的模板参数名称,以提高代码的可读性。

template <typename ElementType, typename AllocatorType>
class MyVector {// ...
};
5.1.2 使用类型别名

类型别名可以简化复杂的类型表达。

template <typename T>
using Ptr = std::shared_ptr<T>;Ptr<SomeClass> myPtr = std::make_shared<SomeClass>();

5.2 调试模板元编程代码的策略

5.2.1 编译器警告和错误

学会阅读和理解编译器的警告和错误信息。

5.2.2 简化模板

将复杂的模板分解为更小的部分,逐一进行测试。

5.2.3 使用static_assert

static_assert可以在编译时检查条件,帮助调试。

template <typename T>
class MyClass {static_assert(std::is_integral<T>::value, "T must be an integral type");// ...
};

5.3 性能优化和陷阱

5.3.1 避免模板膨胀

过度使用模板可能导致模板膨胀,增加编译时间和二进制大小。

5.3.2 内联模板函数

使用inline关键字或模板内联变量来减少函数调用开销。

template <typename T>
inline T addOne(T value) {return value + 1;
}
5.3.3 模板实例化

明智地选择模板实例化,避免不必要的实例化。

5.4 利用constexpr进行编译时计算

5.4.1 constexpr函数

使用constexpr函数进行编译时计算。

template <typename T>
constexpr T pi = T(3.14159265358979323846);template <typename T>
constexpr T calculateCircleArea(T radius) {return pi<T> * radius * radius;
}

5.5 模板元编程与C++标准库

5.5.1 使用标准库中的Type Traits

利用<type_traits>中的类型特性来编写更灵活的模板代码。

template <typename T>
using MaybeConst = typename std::conditional<std::is_const<T>::value, const T, T
>::type;
5.5.2 利用标准库中的元组

使用<tuple><utility>中的元组来处理多种类型的数据。

#include <tuple>template <typename... Args>
std::tuple<Args...> make_tuple_impl(Args&&... args) {return std::make_tuple(std::forward<Args>(args)...);
}template <typename... Args>
auto make_tuple(Args&&... args) {return make_tuple_impl(std::forward<Args>(args)...);
}

5.6 高级模板技术

5.6.1 模板模板参数

使用模板模板参数来参数化模板的模板。

template <template <typename> class TT>
class Wrapper {
public:template <typename T>TT<T> wrapped;
};
5.6.2 变长模板参数的递归展开

使用递归展开技术来处理变长模板参数。

template <typename... Args>
void processArgs(Args... args) {(..., processArg(std::forward<Args>(args)));
}template <typename Arg>
void processArg(Arg&& arg) {// Process single argument
}

5.7 模板元编程的最佳实践

5.7.1 保持模板的简洁性

避免在模板中使用复杂的逻辑,以减少编译时间和潜在的错误。

5.7.2 利用编译时断言

使用static_assert来确保模板的使用符合预期。

5.7.3 编写模板友好的代码

确保你的代码可以很好地与其他模板代码协同工作。

相关文章:

C++模板元编程:编译时的魔法

1. 引言 在C的世界中&#xff0c;模板元编程是一种在编译时执行计算的强大技术。它允许开发者编写高度灵活和高效的代码&#xff0c;这些代码可以在不牺牲性能的前提下&#xff0c;根据类型和值的不同而变化。本文将深入探讨模板元编程的奥秘&#xff0c;并展示如何在现代C开发…...

SQL进阶day10————多表查询

目录 1嵌套子查询 1.1月均完成试卷数不小于3的用户爱作答的类别 1.2月均完成试卷数不小于3的用户爱作答的类别 ​编辑1.3 作答试卷得分大于过80的人的用户等级分布 2合并查询 2.1每个题目和每份试卷被作答的人数和次数 2.2分别满足两个活动的人 3连接查询 3.1满足条件…...

debug调试_以Pycharm为例

文章目录 作用步骤打断点调试调试窗口 作用 主要是检查逻辑错误&#xff0c;而非语法错误。 步骤 打断点 在需要调试的代码行前打断点&#xff0c;执行后会停顿在断点位置&#xff08;不运行&#xff09; 调试 右键“debug”&#xff0c;或者直接点击右上角的小虫子 调试…...

wms第三方海外仓系统:如何为中小型海外仓注入新活力

对于中小型海外仓来说&#xff0c;想在大型集团海外仓同台竞争中获得优胜&#xff0c;提升其管理效率是非常关键的一环。 我们所熟知的wms系统&#xff0c;也就是第三方成熟海外仓系统&#xff0c;正是这些海外仓企业提升管理水平、降低成本的重要工具。 1、wms第三方海外仓系…...

html是什么?http是什么?

html Html是什么&#xff1f;http是什么&#xff1f; Html 超文本标记语言&#xff1b;负责网页的架构&#xff1b; http(&#xff08;HyperText Transfer Protocol&#xff09;超文本传输协议&#xff1b; https&#xff08;全称&#xff1a;Hypertext Transfer Protocol …...

L1-007 念数字js实现

异步解法 const readline require("readline"); const rl readline.createInterface({input: process.stdin,output: process.stdout, }); const input_arr [];//储存数据 rl.on(line, function (line) {input_arr.push(line); } ); rl.on(close, function () {/…...

Perl 运算符

Perl 运算符 Perl 是一种功能强大的编程语言&#xff0c;广泛应用于系统管理、网络编程、GUI 创建、数据库访问等众多领域。Perl 的语法灵活&#xff0c;支持多种编程范式&#xff0c;包括过程式、面向对象和函数式编程。在 Perl 中&#xff0c;运算符扮演着重要的角色&#x…...

语法04 C++ 标准输入语句

标准输入 使用格式&#xff1a;cin >> 输入的意思就是把一个值放到变量里面去&#xff0c;也就是变量的赋值&#xff0c;这个值是由我们自己输入的。 (注意:输入变量前要先定义&#xff0c;输入完之后要按Enter键。) 输入多个变量&#xff0c;与输出类似&#xff0c;…...

python数据分析--- ch6-7 python容器类型的数据及字符串

python数据分析---ch6-7 python容器类型的数据及字符串 1. Ch6--容器类型的数据1.1 序列1.1.1 序列的索引操作1.1.2 加和乘操作1.1.3 切片操作1.1.4 成员测试 1.2 列表1.2.1 创建列表1.2.2 追加元素1.2.3 插入元素1.2.4 替换元素1.2.5 删除元素1.2.6 列表排序&#xff08;1&…...

【Linux取经路】守护进程

文章目录 一、前台进程和后台进程二、Linux 的进程间关系三、setsid——将当前进程设置为守护进程四、daemon——设置为守护进程五、结语 一、前台进程和后台进程 Linux 中每一次用户登录都是一个 session&#xff0c;一个 session 中只能有一个前台进程在运行&#xff0c;键盘…...

Nginx之文件下载服务器

1.概述 在对外分享文件时&#xff0c;利用Nginx搭建一个简单的下 载文件管理服务器&#xff0c;文件分享就会变得非常方便。利 用Nginx的诸多内置指令可实现自动生成下载文件列表 页、限制下载带宽等功能。配置样例如下&#xff1a; server {listen 8080;server_name localhos…...

OpenCV学习(4.11) OpenCV中的图像转换

1. 目标 在本节中&#xff0c;我们将学习 使用OpenCV查找图像的傅立叶变换利用Numpy中可用的FFT功能傅立叶变换的一些应用我们将看到以下函数&#xff1a;**cv.dft()** &#xff0c;**cv.idft()** 等 理论 傅立叶变换用于分析各种滤波器的频率特性。对于图像&#xff0c;使用…...

2024.6.13每日一题

LeetCode 子序列最大优雅度 题目链接&#xff1a;2813. 子序列最大优雅度 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个长度为 n 的二维整数数组 items 和一个整数 k 。 items[i] [profiti, categoryi]&#xff0c;其中 profiti 和 categoryi 分别表示第 i…...

Linux命令详解(2)

文本处理是Linux命令行的重要应用之一。通过一系列强大的命令&#xff0c;用户可以轻松地对文本文件进行编辑、查询和转换。 cat&#xff1a; 这个命令用于查看文件内容。它可以一次性显示整个文件&#xff0c;或者分页显示。此外&#xff0c;cat 还可以用于合并多个文件的内容…...

iOS ReactiveCocoa MVVM

学习了在MVVM中如何使用RactiveCocoa&#xff0c;简单的写上一个demo。重点在于如何在MVVM各层之间使用RAC的信号来更方便的在各个层之间进行响应式数据交互。 demo需求&#xff1a;一个登录界面(登录界面只有账号和密码都有输入&#xff0c;登录按钮才可以点击操作)&#xff0…...

图文解析ASN.1中BER编码:结构类型、编码方法、编码实例

本文将详细介绍ASN.1中的BER编码规则&#xff0c;包括其编码机制、数据类型表示、以及如何将复杂的数据结构转换为二进制数据。通过本文的阅读&#xff0c;读者将对ASN.1中的BER编码有一个全面的理解。 目录 一.引言 二.BER编码基本结构 ▐ 1. 类型域&#xff08;Type&#…...

jQuery如何停止动画队列

在jQuery中&#xff0c;你可以使用.stop()方法来停止动画队列。.stop()方法有几个可选的参数&#xff0c;可以用来控制停止动画的方式。 以下是.stop()方法的基本用法和一些参数选项&#xff1a; 无参数&#xff1a;立即停止当前动画&#xff0c;并跳到最后的状态。后续的动画…...

vue3+electron搭建桌面软件

vue3electron开发桌面软件 最近有个小项目, 客户希望像打开 网易云音乐 那么简单的运行起来系统. 前端用 Vue 会比较快一些, 因此决定使用 electron 结合 Vue3 的方式来完成该项目. 然而, 在实施过程中发现没有完整的博客能够记录从创建到打包的流程, 摸索一番之后, 随即梳理…...

oracle常用经典SQL查询

oracle常用经典SQL查询(转贴) oracle常用经典SQL查询 常用SQL查询&#xff1a; 1、查看表空间的名称及大小 select t.tablespace_name, round(sum(bytes/(1024*1024)),0) ts_size from dba_tablespaces t, dba_data_files d where t.tablespace_name d.tablespace_name grou…...

Android shell 常用 debug 命令

目录 1、查看版本2、am 命令3、pm 命令4、dumpsys 命令5、sed命令6、log定位查看APK进程号7、log定位使用场景 1、查看版本 1.1、Android串口终端执行 getprop ro.build.version.release #获取Android版本 uname -a #查看linux内核版本信息 uname -r #单独查看内核版本 1.2、…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

用鸿蒙HarmonyOS5实现中国象棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...

DAY 45 超大力王爱学Python

来自超大力王的友情提示&#xff1a;在用tensordoard的时候一定一定要用绝对位置&#xff0c;例如&#xff1a;tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾&#xff1a; tensorboard的发展历史和原理tens…...