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{不变:}.

posted @   zjh6  阅读(13)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示