C++元编程语言初步入门详解

目录
  • 模板
    • 泛型初步
    • 函数模板
    • 友元
    • 模板参数
    • 元编程的基本概念
    • 可变参数模板

模板

由于模板元编程需要以面向对象为基础,所以如有疑问之处可以先补充一点C++面向对象的知识:

C++面向对象这一篇就够了

泛型初步

由于C++是静态强类型语言,所以变量一经创建,则类型不得更改。如果我们希望创建一种应用广泛地复数类型,那么相应地需要基于intfloatdouble这些基础类型逐一创建,十分麻烦。泛型编程便是为了简化这一过程而生。

能够容纳不同数据类型作为成员的类被成为模板类,其基本方法为在类声明的上面加上一行模板声明代码

template<typename T>,下一行为class myClass,其调用过程为myClass<T> m。

列举案例如下

#include<iostream>
using namespace std;
template<typename C>
struct Abstract{
    C real;         //real为C类型
    C im;
    Abstract(C inReal, C inIm){
        real = inReal;
        im = inIm;
    }
    void printVal(){
        cout<<"Abstract:"<<real<<"+"<<im<<"i"<<endl;
    };
    Abstract& multi(Abstract val){
        C temp = real*val.real - im*val.im;
        im = real*val.real + im*val.im;
        real = temp;
        return *this;
    };
};
int main(){
    Abstract<float> fTemp{1,2};//C类型为float
    fTemp.multi(fTemp);
    fTemp.printVal();
    system("pause");
    return 0;
}

函数模板

当然,上述multi并不能实现两个不同类型的Abstract之间的相乘,所以可以将multi函数改为

    template<typename T>
    Abstract<C>& multi(Abstract<T> val){
        C temp = real*val.real - im*val.im;
        im = real*val.real + im*val.im;
        real = temp;
        return *this;
    }

这样就能够实现如下功能。

int main(){
    Abstract<float> fTemp{1,2};
    Abstract<int> iTemp{1,2};
    fTemp.multi(iTemp);
    fTemp.printVal();
    getReal(fTemp);
    system("pause");
    return 0;
}

友元

模板类具备一部分普通类的性质,比如struct和class的区别,public、protected、private的性质,以及友元等。模板的声明特性也可以应用在函数中,例如

#include<iostream>
using namespace std;
template<typename C>
class Abstract{
    C real;
    C im;
public:
    Abstract(C inReal, C inIm){
        real = inReal;
        im = inIm;
    }
    void printVal(){
        cout<<"Abstract:"<<real<<"+"<<im<<"i"<<endl;
    };
    Abstract& multi(Abstract val){
        C temp = real*val.real - im*val.im;
        im = real*val.real + im*val.im;
        real = temp;
        return *this;
    }
    template<typename T> friend void getReal(Abstract<T> num);  //声明友元
};
template<typename C>
void getReal(Abstract<C> num){
    cout<<num.real<<endl;
}
int main(){
    Abstract<float> fTemp{1,2};
    fTemp.multi(fTemp);
    fTemp.printVal();
    getReal(fTemp);
    system("pause");
    return 0;
}

需要注意的一点是,在模板类中声明友元,其前缀<typename T>中的类型标识不得与已有的类型标识重复,否则编译无法通过。

由于函数模板可以针对不同的数据类型进行求解操作,是对函数或者方法实例的抽象,所以又被称为算法。

模板参数

如果将模板理解为一种类型声明的函数,那么模板也应该具备一些函数具备的功能。首先其模板参数中可以包含实际类型参数,例如

template<typename T, int max>
class Test{}

其调用时可以写为

Test<int,256> pixel;

模板同样支持默认参数,即可以实现如下形式

template<typename T=int, int max=256>
class Test{}
Test pixle;

除了数据类型、值之外,模板本身也可以作为模板参数,例如下面的形式是合法的。

template<typename T, template<typename> class C>
struct Test{
    C<T>* val;
    Test(C<T>* inVal){
        val = inVal;
    }
};
int main(){
    Abstract<int> fTemp{1,2};
    Test<int,Abstract> test(&fTemp);
    test.val->printVal();
    system("pause");
    return 0;
}

其结果为

PS E:\Code\cpp> g++ .\generic.cpp
PS E:\Code\cpp> .\a.exe
Abstract:1+2i
请按任意键继续. . .

需要注意的一点是,在模板类中定义的模板类,需要进行实例化,否则会出现错误,所以在Test中,以指针形式创建了模板类。

类型函数

以数据类型为输入或输出的函数即为类型函数,在C语言中,sizeof便是一种类型函数,其输入为数据类型,输出为数据类型所需要的内存空间。

在C++11中,using可以实现数据类型赋予的功能,其使用方法与typedef相似

template<typename T>
struct Test{
    using type = T;
}

元编程的基本概念

元编程是泛型编程的一个超集,两者的本质均是针对不同数据类型的算法,后者则更关注传入参数的广泛性。如果将元编程分为四个层次

  • 无计算
  • 运算符连接的运算
  • 编译时具备选择等非递归计算
  • 编译时具备递归运算

那么泛型编程可以作为第一类元编程,或者说更加关注的是参数的传入传出过程,而元编程则更关注不同数据类型的选择过程。

例如,我们可以实现一个最多包含三个元素的元组Tuple,其思路为,三元元素可以看成是一个二元元组与一个参数的组合;二元元组可以看成是一元元组与参数的组合;一元元组则是一个基本数据类型的变量。在这个元组的实现过程中,除了赋值过程实现泛型之外,也需要判断当前所实现的元组元素个数,如果其初始化参量为3个时,需要递归式地创建变量,直到赋值参数为1个。则其实现如下

class Nil{};
//主模板
template<typename T1=Nil, typename T2=Nil, typename T3=Nil>
struct Tuple : Tuple<T2,T3>{
    T1 x;
    using Base = Tuple<T2,T3>;      //三元元组以二元元组为基础
    //返回值为Tuple<T2,T3>指针类型的base()函数
    //static_cast将this转化为Base*类型
    Base* base(){return static_cast<Base*>(this);}
    const Base* base() const {return static_cast<const Base*>(this);}
    //构造函数继承二元元组,在构造本类中x的同时,构造基类Tuple<T2,T3>
    Tuple(const T1& t1, const T2& t2, const T3& t3)
        :Base{t2,t3},x{t1}{}
};
template<typename T1>
struct Tuple<T1>{
    T1 x;
};
template<typename T1, typename T2>
struct Tuple<T1,T2> : Tuple<T2>{
    T1 x;
    using Base = Tuple<T2>;
    Base* base(){return static_cast<const Base*>(this);}
    const Base* base() const {return static_cast<const Base*>(this);}
    Tuple(const T1& t1,const T2& t2):Base{t2}, x{t1}{}
};
template<typename T1, typename T2, typename T3>
void print_elements(ostream& os, const Tuple<T1,T2,T3>& t){
    os<<t.x<<",";
    print_elements(os,*t.base());
}
template<typename T1, typename T2>
void print_elements(ostream& os, const Tuple<T1,T2>& t){
    os<<t.x<<",";
    print_elements(os,*t.base());
}
template<typename T1>
void print_elements(ostream& os, const Tuple<T1>& t){
    os<<t.x;
}
//运算符重载
template<typename T1, typename T2, typename T3>
ostream& operator<<(ostream& os, const Tuple<T1,T2,T3>& t){
    os<<"{";
    print_elements(os,t);
    os<<"}";
    return os;
}
int main(){
    Tuple<int,double,char> x{1,2.5,'a'};
    cout<<x<<endl;
    system("pause");
    return 0;
}

其输出结果为

PS E:\Code\cpp> g++ .\generic.cpp
PS E:\Code\cpp> .\a.exe
{1,2.5,a}

可变参数模板

上述实现过程非常繁琐,而且限制了元组中的元素个数,如果标准库中用上述的书写风格,那么标准库除了这个元组之外也写不了其他的东西了。好在C++模板提供了可变参数的功能,例如,我们可以先将打印模板函数写为

//typename... T 代表可变参数
template<typename T1, typename... T>
void print_elements(ostream& os, const Tuple<T1,T...>& t){
    os<<t.x<<",";
    print_elements(os,*t.base());
}
template<typename T1>
void print_elements(ostream& os, const Tuple<T1>& t){
    os<<t.x;
}
template<typename... T>
ostream& operator<<(ostream& os, const Tuple<T...>& t){
    os<<"{";
    print_elements(os,t);
    os<<"}";
    return os;
}

其输出结果为

PS E:\Code\cpp> g++ .\generic.cpp
PS E:\Code\cpp> .\a.exe
{1,2.5,a}
请按任意键继续. . .

然后将Tuple也做相同的更改

template<typename T1, typename... T>
struct Tuple : Tuple<T...>{
    T1 x;
    using Base = Tuple<T...>;      //N+1元元组以N元元组为基
    Base* base(){return static_cast<Base*>(this);}
    const Base* base() const {return static_cast<const Base*>(this);}
    //注意T&...的书写格式
    Tuple(const T1& t1, const T&... t):Base{t...},x{t1}{}
};
template<typename T>
struct Tuple<T>{
    T x;
};
/*
    print模板
*/
int main(){
    Tuple<string, double,int,char> tt("hello",1.5,1,'a');
    cout<<tt<<endl;
    system("pause");
    return 0;
}

其输出结果为

PS E:\Code\cpp> g++ .\generic.cpp
PS E:\Code\cpp> .\a.exe
{hello,1.5,1,a}

以上就是C++元编程语言初步入门详解的详细内容,更多关于C++元编程语言初步的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++模板元编程实现选择排序

    前言 模板在C++一直是比较神秘的存在. STL 和 Boost 中都有大量运用模板,但是对于普通的程序员来说,模板仅限于使用.在一般的编程中,很少会有需要自己定义模板的情况.但是作为一个有理想的程序员,模板是一个绕不过去的坎.由于C++标准的不断改进,模板的能力越来越强,使用范围也越来越广. 在C++11中,模板增加了 constexpr ,可变模板参数,回返类型后置的函数声明扩展了模板的能力:增加了外部模板加快了模板的编译速度:模板参数的缺省值,角括号和模板别名使模板的定义和使用变得更加的简

  • C++11模板元编程-std::enable_if示例详解

    C++11中引入了std::enable_if函数,函数原型如下: template< bool B, class T = void > struct enable_if; 可能的函数实现: template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; 由上可知,只有当第一个模板参数为

  • 浅谈C++模板元编程

    所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得异常灵活,能实现很多高级动态语言才有的特性(语法上可能比较丑陋,一些历史原因见下文).模板元编程的根在模板.模板的使命很简单:为自动代码生成提供方便.提高程序员生产率的一个非常有效的方法就是"代码复用",而面向对象很重要的一个贡献就是通过内部紧耦合和外部松耦合将"思想"转化成一个一个容易复用的"概念".但是面向对象提供的工具箱里面所

  • C++编程中的函数指针初步解析

    1.定义 每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针. 2.语法 指向函数的指针变量的一般定义形式为: 数据类型 (*指针变量名)(参数表); 3.说明 1) 函数指针的定义形式中的数据类型是指函数的返回值的类型. 2) 区分下面两个语句: int (*p)(int a, int b); //p是一个指向函数的指针变量,所指函数的返回值类型为整型 int *p(int a, int b); //p是函数名,此函数的返回值类型为整型指针 3) 指向函数的指

  • C++元编程语言初步入门详解

    目录 模板 泛型初步 函数模板 友元 模板参数 元编程的基本概念 可变参数模板 模板 由于模板元编程需要以面向对象为基础,所以如有疑问之处可以先补充一点C++面向对象的知识: C++面向对象这一篇就够了 泛型初步 由于C++是静态强类型语言,所以变量一经创建,则类型不得更改.如果我们希望创建一种应用广泛地复数类型,那么相应地需要基于int.float.double这些基础类型逐一创建,十分麻烦.泛型编程便是为了简化这一过程而生. 能够容纳不同数据类型作为成员的类被成为模板类,其基本方法为在类声明

  • python二维码操作:对QRCode和MyQR入门详解

    python是所有编程语言中模块最丰富的 生活中常见的二维码功能在使用python第三方库来生成十分容易 三个大矩形是定位图案,用于标记二维码的大小.这三个定位图案有白边,通过这三个矩形就可以标识一个二维码了. QRCode 生成这个二维码只用一行 import qrcode qrcode.make("不睡觉干嘛呢").get_image().show() #设置URL必须添加http:// 安装导入QRCode pip install qrcode #方法多,体量小 安装导入MyQR

  • OpenMP 共享内存的并行编程框架入门详解

    目录 简介 认识 openmp 的简单易用性 C 语言实现 C++ 实现 OpenMP 实现 opnemp 基本原理 积分例子 总结 简介 OpenMP 一个非常易用的共享内存的并行编程框架,它提供了一些非常简单易用的API,让编程人员从复杂的并发编程当中释放出来,专注于具体功能的实现.openmp 主要是通过编译指导语句以及他的动态运行时库实现,在本篇文章当中我们主要介绍 openmp 一些入门的简单指令的使用. 认识 openmp 的简单易用性 比如现在我们有一个任务,启动四个线程打印 he

  • Python 数值区间处理_对interval 库的快速入门详解

    使用 Python 进行数据处理的时候,常常会遇到判断一个数是否在一个区间内的操作.我们可以使用 if else 进行判断,但是,既然使用了 Python,那我们当然是想找一下有没有现成的轮子可以用.事实上,我们可以是用 interval 这一个库来完成我们需要的操作. 区间判断基础 最基础的区间判断操作就是先创建一个区间几个,然后使用 in 来判断一个数是否存在于区间之内.代码如下: from interval import Interval zoom_2_5 = Interval(2, 5)

  • C语言文件操作的入门详解教程

    一.一些需要掌握的知识点 文件有千千万万,但是在我们的程序设计当中,我们谈的文件一般有两种: 1.程序文件 包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe). 2.数据文件 文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件. 而在本节中,我们主要提到的是数据文件. 1.文件名 我们知道,名字都是用来标识和区别事物的,那么文件名也是这样,是区别各个文件的标识. 一个文件

  • SpringBoot快速入门详解

    Spring Boot是什么? Spring Boot并不是什么新的框架,它是对Spring的缺点进行了改善和优化,Spring Boot默认了很多的框架使用方式,像maven整合了所以jar包同一个道理.及SpringBoot并不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式而已. 为什么使用Spring Boot? Spring Boot官方是这么说的:Spring Boot它是基于Spring开发项目的起点,Spring Boot的设计主要是让你快速地跑起来Spri

  • Java SpringBoot模板引擎之 Thymeleaf入门详解

    目录 模板引擎简介 引入Thymeleaf模板引擎 分析Thymeleaf模板引擎 测试Thymeleaf模板引擎 1.编写一个TestController 2.编写一个测试页面 test.html 放在 templates 目录下 3.启动项目请求测试 4.结论 Thymeleaf入门: 1.修改测试请求,增加数据传输 2.使用thymeleaf 3.我们去编写下前端页面 4.启动测试! thymeleaf语法学习 1.使用任意的 th:attr 来替换Html中原生属性的值! 2.表达式语法

  • 新手必看Android Studio入门详解

    上篇文章已经说过了Android Studio的安装配置,从这里开始我们就来完成第一个Android项目吧! 如何安装配置还不太熟悉的可以参考这篇文章:Android Studio安装配置详细步骤(超详细) 让我们开始第一个Android项目吧 1.建立项目 选一个Empty Activity,然后Next 默认即可,点击 Finish Name:文件名 Save location:文件的保存位置 Language:默认Java,会用Kotlin的也可以更改 API level:默认即可,级别低

  • Java+TestNG接口自动化入门详解

    目录 一.环境准备:(根据自己电脑配置来选择安装版本,我的电脑是64位,所以此处选择64位安装) 二.环境安装: 三.TestNG接口自动化实现 四.创建自己的第一个接口自动化脚本 五.批量执行自动化脚本 六.生成并查看自动化测试报告 一.环境准备:(根据自己电脑配置来选择安装版本,我的电脑是64位,所以此处选择64位安装) JDK下载: JDK 1.8下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downl

  • vite.config.js配置入门详解

    目录 1 如何创建vite项目? 2 如何让vite项目启动时自动打开浏览器? 3vite启动默认端口为3000?如何更改默认端口? 4 vite如何设置热更新? 5vite中如何配置别名路径? 6 vite中如何设置便捷图片路径引用? 7如何把vite打包以后的js,css和img资源分别分门别类在js/css/img文件夹中? 8 如何通过vite给项目配置多个环境? 9 vite中如何配置多入口,进行多页面开发? 10 如何设置开启生产打包分析文件大小功能?类似webpack-bundle

随机推荐