卷积神经网络常用的矩阵工具类。
封装的很好,各类符号都有重载,傻瓜式用法。
也可以作为二维数组来用,不需要非常麻烦地对内存的创建与销毁。
1 | LIBDIR=-L/home/users/0927/systemc-2.3/lib-linux64 |
命令行使用:make
即可编译。
根据上面的方法,添加 Makefile
文件,确保 make
能正常编译。
VSCode 打开项目文件,项目根目录添加 .vscode
文件夹及三个 json 文件,如下:
以下仅介绍这三个文件的用法。
摘录自 feiyangqingyun :https://blog.csdn.net/feiyangqingyun/article/details/104814194
基于 SystemC 的 Fir Filter 示例。
有两个模块,fir 为算法实现,tb 验证数据的输入输出。
使用 vld 和 rdy 来控制 fir 和 tb 之间的握手机制。
1 | SYSTEM |
1 | std::shared_ptr<int> p1; //不传入任何实参 |
注意,空的 shared_ptr 指针,其初始引用计数为 0,而不是 1。
1 | std::shared_ptr<int> p3(new int(10)); |
由此,我们就成功构建了一个 shared_ptr 智能指针,其指向一块存有 10 这个 int 类型数据的堆内存空间。
完美转发指的是:如果 function() 函数接收到的参数 t 为左值,那么该函数传递给 otherdef() 的参数 t 也是左值;反之如果 function() 函数接收到的参数 t 为右值,那么传递给 otherdef() 函数的参数 t 也必须为右值。
某些场景中,我们可能需要限制调用成员函数的对象的类型(左值还是右值),为此 C++11 新添加了引用限定符。所谓引用限定符,就是在成员函数的后面添加 “&” 或者 “&&”,从而限制调用者的类型(左值还是右值)。
移动构造函数的调用时机是:用同类的右值对象初始化新对象。当用当前类的左值对象(有名称,能获取其存储地址的实例对象)初始化同类对象时,需要用到 move() 函数,
move 本意为 “移动”,但该函数并不能移动任何数据,它的功能很简单,就是将某个左值强制转化为右值。
1 | move( arg ) |
其中,arg 表示指定的左值对象。该函数会返回 arg 对象的右值形式。
所谓移动语义,指的就是以移动而非深拷贝的方式初始化含有指针成员的类对象。简单的理解,移动语义指的就是将其他对象(通常是临时对象)拥有的内存资源“移为已用”。
右值的英文简写为“lvalue”,右值的英文简写为“rvalue”。很多人认为它们分别是”left value”、”right value” 的缩写,其实不然。lvalue 是“loactor value”的缩写,可意为存储在内存中、有明确存储地址(可寻址)的数据,而 rvalue 译为 “read value”,指的是那些可以提供数据值的数据(不一定可以寻址,例如存储于寄存器中的数据)。
C++98/03 标准中只能操作左值,无法对右值添加引用(但允许使用常量左值引用操作右值),举个例子:
1 | int num = 10; |
C++11 标准新引入了另一种引用方式,称为右值引用,用 “&&” 表示:
联合体(Union)是一种构造数据类型。在一个联合体内,我们可以定义多个不同类型的成员,这些成员将会共享同一块内存空间。老版本的 C++ 为了和C语言保持兼容,对联合体的数据成员的类型进行了很大程度的限制,这些限制在今天看来并没有必要,因此 C++11 取消了这些限制。
为了解决 const 关键字的双重语义问题,而将“常量”的语义划分给了新添加的 constexpr 关键字。
const 表示“只读”,constexpr 表示“常量”。
常量在编译阶段就能计算出来,提高程序的执行效率。
auto 除了可以独立使用,还可以和某些具体类型混合使用,这样 auto 表示的就是“半个”类型,而不是完整的类型。请看下面的代码:
1 | int x = 0; |
C++11 标准新引入了一种类模板,命名为 tuple(中文可直译为元组)。tuple 最大的特点是:实例化的对象可以存储任意数量、任意类型的数据。
tuple 的应用场景很广泛,例如当需要存储多个不同类型的元素时,可以使用 tuple;当函数需要返回多个数据时,可以将这些数据存储在 tuple 中,函数只需返回一个 tuple 对象即可。
为了统一初始化方式,并且让初始化行为具有确定的效果,C++11 中提出了列表初始化(List-initialization)的概念。
POD 类型即 plain old data 类型,简单来说,是可以直接使用 memcpy 复制的对象。
很久很久以前,C 语言统一了江湖。几乎所有的系统底层都是用 C 写的,当时定义的基本数据类型有 int、char、float 等整数类型、浮点类型、枚举、void、指针、数组、结构等等。然后只要碰到一串 01010110010 之类的数据,编译器都可以正确的把它解析出来。
那么到了 C++ 诞生之后,出现了继承、派生这样新的概念,于是就诞生了一些新的数据结构。比如某个派生类,C 语言中哪有派生的概念啊,遇到这种数据编译器就不认识了。可是我们的计算机世界里,主要的系统还是用 C 写的啊,为了和旧的 C 数据相兼容,C++ 就提出了 POD 数据结构概念。
POD 是 Plain Old Data 的缩写,是 C++ 定义的一类数据结构概念,比如 int、float 等都是 POD 类型的。Plain 代表它是一个普通类型,Old 代表它是旧的,与几十年前的 C 语言兼容,那么就意味着可以使用 memcpy() 这种最原始的函数进行操作。两个系统进行交换数据,如果没有办法对数据进行语义检查和解释,那就只能以非常底层的数据形式进行交互,而拥有 POD 特征的类或者结构体通过二进制拷贝后依然能保持数据结构不变。也就是说,能用 C 的 memcpy() 等函数进行操作的类、结构体就是 POD 类型的数据。
基本上谈到这个概念,一般都是说某某 class、struct、union 是不是 POD 类型的。
可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号…:
1 | template<typename... Types> |
其中,…可接纳的模板参数个数是0个及以上的任意数量,需要注意包括0个。
若不希望产生模板参数个数为0的变长参数模板,则可以采用以下的定义:
1 | template<typename Head, typename... Tail> |
本质上,…可接纳的模板参数个数仍然是0个及以上的任意数量,但由于多了一个Head类型,由此该模板可以接纳1个及其以上的模板参数。