原文
struct Matrix(S, size_t M, size_t N)
{
}alias Vec3(S) = Matrix!(S, 3, 1);
void foo(U)(U v) if (is(U == Vec3!S, S))
{
}void bar(U)(Vec3!U v) {}
void main()
{import std.stdio;Vec3!float v;writeln(is(typeof(v) == Vec3!S, S)); // 假// bar(v); // 错误,不能用Matrix!(float, 3, 1). huh!调用bar// foo(v); // 错误,不满足约束
}
这与DIP1023
有关.
因为目前不能用模板
别名参数,那么如何更直接编写约束
?
这像是有缺陷的设计:
writeln(is(Vec3!float == Vec3!S, S)); // 假
我可忍受比这更多的宏.
我也不喜欢再次编写矩阵!(S,3,1)
.
我最近遇见了类似的问题.
import std;
static assert(isInstanceOf!(Array, Array!char)); // 真
static assert(isInstanceOf!(Regex, Regex!char)); // 假
问题是编译器非常快速地用Matrix!(float,3,1)
重写typeof(v)
,但编译器无法分辨出
Matrix!(float, 3, 1)
就是Vec3!S
,对某些通用S
(它无法弄清楚S应该是什么).
对特定的S
,可以.如writeln(is(typeof(v)==Vec3!float));
打印真
.所以像
void foo(U)(U v) if (is(U == Vec3!float) || is(U == Vec3!double)) {}
即使它更尴尬,但有效
.
需要某种反向
通知编译器即矩阵!(float,3,1)
也是Vec3!float
的方法.
根本问题是,需要某种
方法来告诉编译器
,从某个具体
类型到模板别名
的映射.
在此讨论
编译器不能重写或扩展Vec3!S
到矩阵!(S,3,1)
然后轻松求值:
is(Matrix!(float, 3, 1) == Matrix!(S, 3, 1), S) // 真
DIP1023
是关于解析
模板别名的.编译器当前为了调用
函数按不同
事物,处理Vec3!S
和矩阵!(S,3,1)
.DIP1023
试重写Vec!S
为矩阵!(S,3,1)
.
问题是:
alias Vec3(S) = Matrix!(S, 3, 1);
//实际是
template Vec3(S) {alias Vec3 = Matrix!(S, 3, 1);
}
的缩写
,表明,这是非常强大
的,但可能不是你想要
的,但可很容易地写:
template Vec3_alt(S) {static if (!is(S == int))alias Vec3_alt = Matrix!(S, 3, 1);elsealias Vec3_alt = real;
}
此时,你如何解析Vec3!S
,这并不像仅按矩阵!(S,3,1)
重写那么简单.
DIP1023
中的方法仅适合D支持的模板别名
功能的有限(尽管很常见
)子集
,因此它是模板别名
推导的特殊大小写
.这一点,再加上"为什么不只使用模板约束
"问题,就很难获得支持.
它看不到这一点.
问题
是,你正在通过单向
窗口查看.一般,编译器无法解决它,因为它不知道模板
和得到
的东西之间的关系
:
alias Foo(T) = int;
//以下对编译器等价
is(Foo!float == Foo!T, T);
is(int == Foo!T, T);
编译器应如何假定T
?当它看到类型
时,它以int
出现,别名模板
自身已完全消失了.
现在,我相信编译器可为简单
别名做个特例
,但必须定义规则
,且语言设计者要同意
添加它.
这是个令人沮丧
的限制.大约16
年前我提出了一个,这里.
它看起来很容易
解决,但实际很难.
与用宏
重写代码后,试向后
使用C预处理器宏
一样.
但总会有编译器
无法弄清楚的情况,因此会有些混乱
.
在实例化
模板前,编译器不知道模板
是别名
.阅读代码
很明显.但内部,不会存储
它.
SHOO
的例子更是无法解决.isInstanceOf
自身就是个模板
,因此是缓存
的.根据D语言
规则,别名
不能导致不同
实例化.
alias Foo(T) = T;
alias Bar(T) = T;pragma(msg, isInstanceOf!(int, Foo)); // 应为真
//如果这是真,现在缓存`它们`,因为它们与第一个等价
pragma(msg, isInstanceOf!(Bar!T, Foo));
pragma(msg, isInstanceOf!(Foo!T, Foo));
//不应根据调用顺序决定求值
-史蒂夫
要解释机制:
编译器
知道用来
实例化的,在模板
内保存的结构
的模板参数
是什么,因为此信息与结构
一起存储.可完成,因为,且仅因为:在实例化模板
中只有1个
结构.也即,可精确地从结构和类
,恢复两个模板参数
:
template Foo(T) {struct Foo { }
}
可这样,因为且仅因为,Foo
结构的词法
父级是带T
的Foo
模板.对有唯一
存储词法父级
属性的类型,可恢复
模板参数;否则,不行.
如下不行:
template identity(int i) { enum identity = i; }void foo() {enum five = identity!5;static assert(is(five == identity!y, int y) && y == 5);
}
它不管用,因为five
不会按父项
在词法
上引用同一
,因为five
是int
,而int
不是与同一
模板唯一
关联的类型.
你需要的是全新
的类似类型类
的语言功能
.现在有添加
类型类到语言
中的讨论,如,它们会使区间
更加时髦
,但这不能使模板推导
稍微更强大.
但是如果使它适合
结构,(带些额外
的样板)它会适合每种
类型:
template identity(int i) {struct wrapper { int w = i; }enum identity = w;
}void foo() {enum five = identity!5;static assert(is(five == identity!y, int y) && y == 5);
}
通过一些
新的降级
,甚至可摆脱该样板
.
如,如果检测
到这样
使用它,只需自动
添加"父"
属性到模板参数
中,(但需要一些编译时间).
"某处"
定义,因为可用foo
的不同编译器调用
中编译同一
.不能给int
添加父属性
,因为那将是不同
类型.可在结构
中包装int
,可在结构中包装所有内容
,然后is
今天就可工作了.但这很麻烦.
为什么不直接从Rust
中窃取特征
,那么Matrix
可仅当N=1
且函数只可接受Vector
参数,才能实现向量
.
注意,即使is(five)
是错误
的,因为五
不是类型
.
因此,即使模式匹配
成功,上面的静断
也是假的.(我看到另一个D用户
说,他认为is
可测试类型
实例,它不能
).为了匹配值模式
,需要另一个构造,也许是匹配
式.
好的
alias five = identity!5;
也不行.(如果不是同名
模板,推导式
可工作).
我想如果举一个可能有效
的示例,为什么它不行,就更加明显了:
alias identity(int i) = int;void main() {alias five = identity!5;static assert(is(five == identity!y, int y) && y == 5);
}
因为five
是个类型
,而不是一个式
,且它是一个没有父级词法
的(int)
类型.
好吧,别名
与原符号
工作方式相同,这就是问题
所在.你无法推导生成别名
的模板实例化,因为"别名成员"
与"模板结构"
,不是相同
的词法成员
,编译器看不到别名
.
像类型特征
或类型类
等会更简洁
的满足这一需求,并使许多区间
代码更好.