d不变模板特化问题
原文
考虑不变构:
不变 struct S { }
定义比如用Unqual
从类型
中去除不变
的模板特化
.
mixin template Foo(T) { static assert(is(S == T)); }
mixin template Foo(T: 不变 U, U) {
pragma(msg, U.stringof);
//即使匹配,S应也是S,只有一个S.
static assert(is(S == U));
//错误::`is(不变(S)==S)`为假
}//T为不变的U
//`Foo!(不变(S), S)`从这里实例化.
应用至不变结构
:
mixin Foo!S;
从2.076.1
开始,它选第二个
模板,且因静断
而失败!设法取结构类型
并去掉其固有常
属性.
这很糟糕.如果按不变 构 S
定义S
,则不应取得非不变
的S
.
可能相同错误
的不同
实例:
不变 struct Struct { }
struct Wrapper {
void foo(U)(inout(U)) inout {
// 按'struct Struct'而不是'不变 struct Struct'推导U.
// 证据:
pragma(msg, isMutable!Struct.stringof ~ " - " ~ isMutable!U.stringof);
static assert(is(U == Struct));
}
}
Wrapper().foo(Struct());
按'struct Struct'
而不是相应不变版
推导U
.
注意,即使从结构类型
自身中去掉
不变,它仍然应用至
结构成员:
不变 struct S { int n; }
static if (is(S : 不变 U, U))
{
static assert(!is(U == 不变));
//U非不变,
static assert(is(typeof(U.n) == 不变));
//但U.n仍是不变.
}
我不认为
结构有"固有常
属性".规范说:
这里
结构声明
可有const,不变
或者shared
存储类.它与按const或不变或shared
声明结构
的每个成员
效果相同.
所以应同等
对待如下结构:
不变 struct S0 { int n; }
struct S1 { 不变 int n; }
现在请注意,即使对S1
,is(S1==不变(S1))
也不成立.真正
问题是:
pragma(msg, S0); // 不变(S0)
pragma(msg, S1); // S1
按名
引用S0
会产生不变(S0)
类型,而不仅
是具有不变
成员的S0
.
1,2.
似乎是预期
的行为.
仍然很糟糕.(及违反
规范.),即语言无法区分不变 构 S {}
与S {}
的不变 S
.
附加常
到类型
暗示了应该
如何使用该类型
.如,对返回Nullable
的模板函数
,我希望第一个
返回Nullable!(不变S)
,即Nullable!S
,而第二个
返回不变 Nullable!S
.由于该错误
,目前不行.
D的类型系统
目前保证
,对合格的Q(T)
类型,存在相应不合格
的T
类型.为此保证
引入特例
异常,可能会破坏
至少与修复
一样多的代码
(在Phobos
中,git grep 'Unqual!'
来试试).
如果想向用户
提示默认,应用特定符操作类型
,更好方法(不考虑极端
)是,对私有
限定版本
用公开别名
类型.
//lib.d
module lib;
private struct StructImpl { }
alias Struct = 不变(StructImpl);
//app.d
import lib;
Struct s;
static assert(is(typeof(s) == 不变));
:Q(T)
…,
我不同意
该说法,特别是因为
有不变 构 S
.Unqual
表示它去除了类型
限定符.但是不变 构
并不是限定符
,它只是"不变
"声明.显然,目前是按隐式限定符
实现的,这是该错误
意义所在,不符合规范
.
有不合格
类型有什么意义?它与类型
的可变
转换值不同.(那需要头可变
.)因此,不能造"可变T字段
",这无意义.
D未提出要求,Paul
提出了要求.它按如修饰名
的修改
类型来定义不变
,这里:
编译器用它标识
类型.另见按顶级Type
类中字段实现
限定类型符
,这里:
想发明新的带自身混杂
的"不变构
"概念?因为我不喜欢复杂化类型系统
,所以正在寻找
无特例方法.
不变属性规范
它说不变
与常
一样,所以看一下常
部分,这里:
const
属性从T
更改声明符号类型
到const(T)
,T为无常
时为引入
符号指定
(或推导
)的类型.
本段
隐含假定声明
符号具有
类型.但是在不变 构 S
中,就,S
没有类型
,它*是*
类型.因此,规范并没有明确
回答"不变构S
"的意思.
问题是Unqual
似乎有相互矛盾
的目的,这最终是关于Unqual
的,关于取类型
,并取可变版本
的能力.
问题是,一方面,不变
表示"每个字段
都标记为不变
".但标记字段
为不变
废弃了Unqual
概念,因为声明Unqual!T
,将不再是可变
值.
好的,因此认为只是有,可变
和不变
类型,及是一般无用的Unqual
特殊工具.但如何从类型
中去掉不变
?
因为现在必须区分"按不变
标记类型"和"按不变
标记的类型声明
".因为,既然有不能转为可变
的类型声明
.则不能从不变 构 S
等去除不变
.
因此,我要区分'不变 构 S'
和'构 S,不变 S'
,因为:
1,用户想有普通 S
,尤其是在构造
类型,按内部
字段用时;即,就像不变 可无效!S
,尽量扩大不变
.
2,用户断定永远不处理可变 S
.
这是对
的,因为这给了带管用
不变量的纯值
类型.域
类型,不必在访问器
后面隐藏
每个字段,因为*只能*
通过构造器设置
字段.因为适合域类型
.这就是不变 构 S
的用途.
正如丹尼斯
所说:减少
类型系统中的特例
.
忘记Unqual
.它只是示例
(显然不好).避免类型系统
中的特例
是重点.
考虑:
class PassengerWagon;
class CargoWagon;
struct Wagon {
PassengerWagon passengerWagon;
CargoWagon cargoWagon;
}
近似
求和类型,但问题是,如,货车
既可以是乘客
也可以是货车
.而显然货车
不能这样.这是建模
错误.
用不变量
来解决它:
struct Wagon {
PassengerWagon passengerWagon;
CargoWagon cargoWagon;
invariant((passengerWagon is null) != (cargoWagon is null));
}
现在当然这是非常危险
类型!因为如果你
auto wagon = Wagon(passengerWagon, null);
wagon.passengerWagon = null;
无声地违反
了不变量
,因而是漏洞
之源.此外,隐式
结构构造器
不检查不变量
,所以可做一些
愤怒的事情,比如Wagon()
.
所以加强它!
struct Wagon {
private PassengerWagon passengerWagon_;
private CargoWagon cargoWagon_;
invariant((passengerWagon_ is null) != (cargoWagon_ is null));
@disable this();
this(PassengerWagon passengerWagon, CargoWagon cargoWagon) {
this.passengerWagon_ = passengerWagon;
this.cargoWagon_ = cargoWagon;
}
PassengerWagon passengerWagon() {
return passengerWagon_;
}
CargoWagon cargoWagon() {
return cargoWagon_;
}
}
注意到,该简单类型
开始烦人
.即,可怕.可读性
差,写起来
糟糕,且很容易出现错别字
.
我们在很多领域有很多
这样结构
.太浪费时间.
我引入了样板
来修复它:
struct Wagon {
@ConstRead
private PassengerWagon passengerWagon_;
@ConstRead
private CargoWagon cargoWagon_;
invariant((passengerWagon_ is null) != (cargoWagon_ is null));
// 里面,仍然生成构造器.
mixin(GenerateThis);
mixin(GenerateFieldAccessors);
}
现在它更短了,但仍然有点烦人
,而且,当构建
时,机器都会耗尽
内存.样板文件中的数千行
模板与此有关
吗?可能永远不会知道.
但是真正想要
的不就是强制
所有字段
都只能用构造器
设置吗?
不是已经有了确保字段
不会改变
的D语言
功能吗?
immutable struct Wagon {
PassengerWagon passengerWagon;
CargoWagon cargoWagon;
invariant((passengerWagon_ is null) != (cargoWagon_ is null));
mixin(GenerateThis);
}
漂亮而简单
,没有访问器
,基本上没有模板
,没有绕过
不变量的意外改变
.
当然,有不变
的大量错误列表
.
顺便,有内部实现哈希映射
!不是为了性能,只是为了处理不变
类型.
就是那样的简单结构
,带简单注解
,零
模板开销,太诱人了,不变
的纯数据.它应该是编写安全,可信赖
代码的关键组成部分
.
如前,不变
构就足够了.类型
虽然去掉了不变
,但内部成员,没有去掉不变
.
为了区分,不变 构 S
与构 S{不变:}
,你可能引入新特例
.我错过了什么吗?
这管用吗?
struct S {
immutable:
// 字段...
}
既然如此,就应该让不变 构 S==构 S{不变:}
.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现