d的编译时成本
创建2022
年dconf
在线演示时,我在找构建约束
的替代
方法.在此验证概念.
我开始研究标准库的成本,考虑isInputRange
的约束条件:
enum bool isInputRange(R) =
is(typeof(R.init) == R)
&& is(ReturnType!((R r) => r.empty) == bool)
&& (is(typeof((return ref R r) => r.front)) ||
is(typeof(ref (return ref R r) => r.front)))
&& !is(ReturnType!((R r) => r.front) == void)
&& is(typeof((R r) => r.popFront));
只看返回类型(ReturnType)模板?它接受参数
(本例中是个λ
函数)并计算
可调用对象的返回类型
.
但是语言有此功能,不是吗?是的,它叫typeof.typeof
提供式的"类型"
,并且不需要额外
语义计算,直接链接
到编译器的语义分析
.
看一下返回类型(ReturnType)
模板(及其依赖):
template ReturnType(alias func)
if (isCallable!func)
{
static if (is(FunctionTypeOf!func R == return))
alias ReturnType = R;
else
static assert(0, "无中类型");
}
template FunctionTypeOf(alias func)
if (isCallable!func)
{
static if ( (is(typeof(& func) Fsym : Fsym*) && is(Fsym == function)) || is(typeof(& func) Fsym == delegate))
{
alias FunctionTypeOf = Fsym;
// 嵌套
}
else static if (is(typeof(& func.opCall) Fobj == delegate) || is(typeof(& func.opCall!()) Fobj == delegate))
{
alias FunctionTypeOf = Fobj;
// 可调用
}
else static if (
(is(typeof(& func.opCall) Ftyp : Ftyp*) && is(Ftyp == function)) ||
(is(typeof(& func.opCall!()) Ftyp : Ftyp*) && is(Ftyp == function))
)
{
alias FunctionTypeOf = Ftyp; // 可调用
}
else static if (is(func T) || is(typeof(func) T))
{
static if (is(T == function))
alias FunctionTypeOf = T; // 函数
else static if (is(T Fptr : Fptr*) && is(Fptr == function))
alias FunctionTypeOf = Fptr; // 函数指针
else static if (is(T Fdlg == delegate))
alias FunctionTypeOf = Fdlg; // 闭包
else
static assert(0);
}
else
static assert(0);
}
template isCallable(alias callable)
{
// 20行
}
template isSomeFunction(alias T)
{
// 15行
}
哇,为什么这么复杂
?为确定
返回类型,要用typeof
原语,但这需要有效式
.对可调用
对象,表明需要一组有效参数
.这些都需要库来内省
,但只给了库
一个符号
而没有环境
.
但是却有环境
!使用R
可确切
知道如何
调用构造的λ
函数!
ReturnType
可分发各种调用,而不仅是λ
.
但是要生成式,是输入区间
不需要构造
,甚至不需要有效的R
,它只需要现有的R
来调用它.
可用null
转R*
,就有了"现成"
的R
.是的,运行它会崩溃,但不需要运行
它,只需要得到它的类型
!
如下是是输入区间
不用ReturnType
的等价模板:
enum isInputRange(R) =
is(typeof(R.init) == R)
&& is(typeof(() { return (*cast(R*)null).empty; }()) == bool)
&& (is(typeof((return ref R r) => r.front)) ||is(typeof(ref (return ref R r) => r.front)))
&& !is(typeof(() { return (*cast(R*)null).front; }()) == void)
&& is(typeof((R r) => r.popFront));
这里区别是有个无参
的λ
,所以不必依赖库技巧
或内省
来知道如何调用
它.
衡量结果
给定与std.traits
完全独立的是输入区间
,结果如何?节约了多少?答案为:节约50%
时间,65%
空间.
每次调用返回类型
是独立
的,因此执行自己的语义分析
.使用编译器的v模板开关,可看到使用当前的标准库
添加了相当多的依赖模板
.
结语
使用std.traits
时,要考虑编译
时间成本.
这没有简化ReturnType
,它只是普通的λ
函数.我现在这样:
template RT(alias sym) {
static if(is(typeof(sym) R == return))
alias RT = R;
else
static assert(false, "bad");
}
...
else version(useIsExpr)
{
enum isInputRange(R) = is(typeof(R.init) == R)
&& is(RT!((R r) => r.empty) == bool)
&& (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front)))
&& !is(RT!((R r) => r.front) == void)
&& is(typeof((R r) => r.popFront));
}
结果仍然不比直接
使用typeof
好,但是要好得多
,与直接使用typeof
相比,对10000
个实例,它增加了大约0.15s
的总编译时间
,及增加了100MB
的内存使用.
观点仍然成立:在约束模板
中,应尽量
避免使用各种方便
的模板.只要给出正确
答案,没人关心'isInputRange'
的实现.
现在,亚当
有一个观点,如果已在其他地方
使用了该方便的模板
,那么它会对整体性能
产生负面影响,因为模板答案的缓存
可加快编译速度
.此时,保证模板实例化
是唯一
的,因为这些都是λ
式,所以这不适用
.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现