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{不变:}.
浙公网安备 33010602011771号