d的两个编译时
d的编译时
两个阶段:ast
与ctfe
,ast为生成树,ctfe为编译时求值.其实很简单啦.ast树,必须通过语义给树加上意义
,然后求值函数才能求值
,对不对?不然,光是树,是没用的.
其中,模板变函数,遇见了,先造一个ast的模板
,然后实例化时,就填充这个ast
,然后加入整个生成树里面
.
ctfe
,在语义分析
与代码生成阶段
.编译时求值
就是把一些编译时已知可以求值
的东西,求出来,这样运行时就不必再求了.
因而,ctfe
不能访问ast阶段功能
,同样,ast
无法访问ctfe
时功能.
模板变函数
首先变成ast(代表程序结构,及所有可知道的东西)
,此时的ast
不含变量,内存,输入,输出
.模板变函数,在ast操作阶段完成.因此必须知道编译时模板参数,另一个是静如
,用于选择ast子树
.
struct S(bool b)
{
static if (b)//根据b选择
int x;
else
float y;
}
ctfe(编译时求值)
其发生在语义赋值
完成后,想想,这些树语义都没有,你拿什么去求值?
常量折叠
,更复杂的函数
,d都可以执行.基本上对纯函数
都非常好.ctfe
可简化一大堆没必要的动作.甚至字节码解释器
来操作.
强制编译时求值
int complicatedComputation(int x, int y)
{
return ...;//复杂计算
}
void main()
{
int i = complicatedComputation(123, 456);
//编译器可根据计算复杂度选择是否在编译时求值
enum j = complicatedComputation(123, 456);
//强制编译时求值
}
案例
int ctfeFunc(bool b)
{
static if (b)
return 1;
else
return 0;
}
enum myInt = ctfeFunc(true);
解决方法
:
1:int ctfeFunc(bool b)()
.
2:
template Value(bool b)
{
static if (b)
enum Value = 1;
else
enum Value = 0;
}
3:
int ctfeFunc(bool b)
{
if (b)//关键在后面的`enum`看见后要`编译时求值`
CTFE引擎实质是模拟运行时先算
,这样运行时就不必再算了
,ast->ctfe,然后生成简化的ast
.可以一个个子树的ast
,当然在所有可能语义相关的东西都准备好时,再ctfe
.
案例
template MyTemplate(T)
{
pragma(msg, "用T实例化=" ~ T.stringof);
//没问题
}
//有问题的下个
int ctfeFunc(int x)
{
if (x < 100)
return x;
else
{
pragma(msg, "bad value passed in");
return -1;
}//按道理,根本就没生成这个else块
//但打印了两下,说明pragma(msg,...)是在ast构造阶段,还未赋值语义.因而不考虑其周围块是什么东西.运行,就在该阶段运行.而枚举编译时求值后,则可能没有了(该else块ast子树被删了).
}
enum y = ctfeFunc(50);
案例
著名的__ctfe
,在ctfe求值阶段为1,在运行时为0
.
int[] buildArray()
{
static if (__ctfe) //错误例子,应改为下行,
if (__ctfe) //正确,`ctfe执行`
{//在ctfe外部,其值恒假,编译器会删掉.
int[] result;
foreach (i; 0 .. 1_000_000)
result ~= i;//ctfe中,~=即可附加
return result;
}//相当于我生成了一个快速版本,但后面我删掉了自己,ctfe是重量级的代码
else
{
import std.array : appender;
//运行时,用appender更快
auto app = appender!(int[]);
foreach (i; 0 .. 1_000_000)
app.put(i);
return app.data;
}
}
出错原因:statif if 发生在ast构造时
,__ctfe发生在语义赋值后
案例
void func(Args...)(Args args)
{
foreach (a; args)//在ast操作阶段循环展开
{//编译时,对每份参数生成一个子树,有N份ast.
static if (is(typeof(a) == int))
{
pragma(msg, "is an int");//始终要打印
continue;//ast操作阶段,未解释
}
pragma(msg, "not an int");
else pragma(msg, "not an int");//解决方法.
//始终要打印
}//pragma,无视周围
}
void main()
{
func(1);//1,ifti(隐式模板实例化,即推导出模板类型了)
}
每一
在类型列表
时,不解释断
与下
.
最后
import std.stdio;
void func(Args...)(Args args)
{
foreach (arg; args) //foreach类型列表
{
static if (is(typeof(arg) == int))
continue;
writeln(arg);
static if (is(typeof(arg) == string))
break;
writeln("参到达循环尾", arg);
}
}
void main()
{
func(1, "abc", 3.14159);
}
//再看,比较:
import std.stdio;
void func(Args...)(Args args)
{
foreach (arg; args)
{
static if (is(typeof(arg) == int))
continue;
writeln(arg);
static if (is(typeof(arg) == string))
break;
//因为串打断循环了,该为真?
static assert(!is(typeof(arg) == string));
//如`断`工作,则不应有这句,不应,应真?
//要静断
}
}
void main()
{
func(1, "abc", 3.14159);
}
//这是编译器的输出,即经过静断这句话
test.d(16): Error: static assert (!true) is false
test.d(21): instantiated from here: func!(int, string, double)
在foreach
的ast操作阶段
,未解释断
和下
,简单视为节点,输出:
@safe void func(int _param_0, string _param_1, double _param_2)//代码生成阶段
{
import std.stdio;/*展开*/
{
{
int arg = _param_0;
continue;//下
writeln(arg);
}
{
string arg = _param_1;
writeln(arg);
break;//断
}
{//删掉了
double arg = _param_2;
writeln(arg);
}
}
}
//变成
void func!(int, string, double)(int __arg0, string __arg1, double __arg2)
{//代码生成期优化掉了,
writeln(__arg1);
}
你搞懂了吗?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现