网资酷

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 79|回复: 2

泛型编程(3)

[复制链接]

4

主题

5

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2023-1-16 22:07:07 | 显示全部楼层 |阅读模式
元编程是操作程序的程序。
编写元程序的语言称之为元语言,被操纵的语言称之为目标语言。根据元语言和目标语言是否相同,我们可以将元编程分为两类:


当元语言即目标语言本身时,元编程是目标语言所支持的高级特性,是在编译期或运行期生成或改变代码的一种编程形式,是狭义上的元编程;当元语言并非目标语言时,元编程侧重代码内容的生成,并不关注目标语言代码的编译和执行,也可以称之为产生式编程(Generative Programming)或者代码生成技术(Code Generation)。

因此,元编程又被成为两级编程(two-level programming)生成式编程(generative programming)模板元编程(template metaprogramming)

作为二级语言的c++


令人惊异的是,模板与其他一系列C++特征一起构成了一种图灵完备的、C++的编译时子语言。这就使C++成为一种二级语言 : C++程序可能同时包含静态代码(它在编译时被求值)和动态代码(编译后,在运行时执行), 如前面所强调的,这个静态子语言是图灵完备的,就是说, 它同时包含一个条件和一个循环结构, 并且因此可以被用来编码任意的算法,这些编码由编译器在编译时而被解释。
主要的编译时条件结构是模板特化(名为模板特化实为模式匹配):  编译器必须在几个选项中, 选择匹配的模板。编译时循环结构就是模板递归,  例如,类模板的一个成员被用于它自己的定义中。而且,C++对于一个程序的哪一部分将在编译时求值,和哪一个在运行时执行都有精确的规则。例如, 用来初始化C++的某些的静态部分的一个整型表达式, 将在编译时被求值。这些静态部分例如整型模板参数或者--个枚举类型的一个成员。这些要素使C++成为一种二级语言。
c++包含了一种精巧的模板机制。借助模板c++可以在编译时进行数值计算,类型操纵,代码生成,计算出运行时所需要的数据。
目前 C++ 将模板分成了 4 类:类模板(class template)函数模板(function template)别名模板(alias template)变量模板(variable template)。前两者能产生新的类型,属于类型构造器(type constructor);而后两者仅是语言提供的简化记法,属于语法糖(syntactic sugar)
来看第一个例子(cpp20起步),我们来计算一个自然数的阶乘的静态代码(编译期执行)
consteval size_t factoral(int n) {
  return (n == 0) ? 1 : n * factoral(n - 1);
}

int main() {
  std::cout << "factoral(7): " << factoral(7) << '\n';
}
consteval 说明符声明函数或函数模板为立即函数,即该函数的每次潜在求值的调用(即不求值语境外的调用)必须(直接或间接)产生编译时常量表达式。
有了上述保证,编译器在处理对consteval函数的调用时,将保证在编译期计算出函数返回值,从而在生成的代码中不包含任何对consteval函数的调用。我们不再需要通过模版特化和递归来做编译期计算了,我们直接通过新的关键字consteval来实现编译期计算,它修饰一个函数,表明这个函数是在编译期计算的,这个函数和一个普通函数看起来几乎没有分别,唯一的差别就是多了一个consteval,
如果是版本稍低一下的c++又如何那?
template<int N>
struct factoral
{
        constexpr static int value = N * factoral<N - 1>::value;
};

template<>
struct factoral<0>
{
        constexpr static int value = 1;
};

int main()
{
        std::cout << factoral<7>::value <<'\n';
        std::cout << factoral<4>::value << '\n';
        std::cout << factoral<5>::value << '\n';
}

// 最开始cpp只能用枚举进行元编程
template <int n>
struct factoral
{
  enum {
    value = factoral<n - 1>::value * n
  };
};

// 终止条件
template <>
struct factoral<0>
{
  enum {
    value = 1
  };
};

int main() {
  std::cout << "factoral(7): " << factoral<7>::RET << '\n';
}
编译期实例化 factoral<0>...factoral<7>最好它的值是5040,
但是有个问题
std::cout << factoral<7>::value <<'\n';        
std::cout << factoral<4>::value <<'\n';        
std::cout << factoral<5>::value <<'\n';每次实例化的生成了重复的代码,读者可以思考一下在已经实例化了factoral<7>,以后计算factoral<4>是否可以复用。
我们可以把factoral<>看作一个函数,它在编译时被求值。这个特殊的函数把一个数值作为参数,并且在它的value成员里返回另一个数值。这样的函数的参数和返回值也可以是类型。
你可能注意到了,这个函数和一个if语句相对应:它有一个条件参数,一个“then"参数Then,和一个”else"参数Else, 就是在value里面返回Then,如果condition是false,它就在value中返回。它是在模板特化中编码的.
请注意,IF<>使用了部分模板特化,我们只需要对condition==false 条件进行特化,而Then和Else仍然在模板特化中保证变化,嗯嗯,你应该感觉出这个东西像什么了。
template <bool condition, class Then, class Else>
struct IF
{
   using RET = Then;
};

template <class Then, class Else>
struct IF<false, Then, Else>
{
   using RET = Else;
};

int main() {
  IF<(2+2>4), short, int>::RET i;
  std::cout << sizeof(i) << '\n';
}
如你所见,静态c++代码和动态c++代码俨然不同,编写静态c++代码和使用一种函数式编程类似,
下面总结了c++在静态层次上可用的编程机制:
把类模板当作函数
类型和值作为数据
符号名代替变量
常量初始化或using/typedef语言代替赋值
模板递归代替循环
条件操作符或模板特化作为条件构造
这些内容下一章再讲。
回复

使用道具 举报

1

主题

4

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2023-1-16 22:07:24 | 显示全部楼层
学姐总结的好啊[赞同]
回复

使用道具 举报

1

主题

4

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2023-1-16 22:07:43 | 显示全部楼层
模板元编程是元编程的方式之一,利用c++的模板,两者不是相等的关系,用宏其实也可以实现简单的元编程。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|网资酷

GMT+8, 2025-3-15 08:14 , Processed in 0.094721 second(s), 23 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表