d区间函数属性实践

原文

import std.algorithm;

struct Foo(R) {
    R r;
    int i;

    bool empty() @nogc nothrow pure @safe scope {
        return r.empty;
    }

    auto front() @nogc nothrow pure @safe scope {
        return r.front;
    }

    auto popFront() @nogc nothrow pure @safe scope {
        r.popFront();
    }
}
//Foo尽量加属性.但不能为常,因为`popFront`是可变的.
auto foo(R)(R r) {
    return Foo!R(r);
}

int count;

void main() {
    [ 1, 2 ]
        .map!((i) {
            ++count;    // <-- 不纯
            return i;
        })
        .foo;
}

r.front是不纯的,λ@nogc也会有类似编译错误.
因为Foo是模板,它不应在成员函数上放属性?还是仅使用依赖模板参数成员的成员函数?是模板的非成员?
不纯工作不好,其余好的.
是否放函数属性单元测试块上来抓此类问题?

@nogc nothrow pure @safe
unittest
{
    // ...
}

是的.除了@trusted,模板代码上的显式属性有问题.
要测试特定代码是否是的,这样:

static assert(!__traits(compiles, () pure {
    // 代码
});

pure,@nogc,nothrow,等属性@safe都应该留待推理.函数要么可执行这些属性,要么不能.
constinout是不同的属性,这些不是推导的,你要自省来确定.这真的很不幸,因为没有简单方法可以"如果允许,则为const",你必须重复实现.

这并不可怕,因为我不确定传递不纯函数给模板会怎样.
你不必测试编译器推导,只要期望它可工作.你要测试的是,你为Foo编写的代码是否使应该纯的东西不纯.
因此,如,创建纯nogc,nothrow,安全的虚区间,并按该区间的包装器测试Foo,给unittest加上属性.如果管用,应该很好.不应测试如果传入不纯函数,就会不纯.
如果有基于不同属性编译的代码,你也应该都测试,确保覆盖相关代码.

我认为,应该省略模板聚集成员的属性,*除非*你想在所有实例上确保该属性.如,如果语义要求,某方法不应改变状态,那么可在其上放const.
否则,我会让编译器推导实际属性,并在模板代码中用适当制作单元测试来抓属性违规.这样来最大化一般性:
1,如果用户想用你代码,并与他们自己的数据类型一起,但需要不纯或抛,那么你模板应该"优雅地降级"而不是拒绝编译(因为用要抛的.front实例化Foo编译会是错误).为此,必须让编译器尽量多地推导属性.如,对不抛.front的实例化,它会推导出不抛.但是对涉及的用户类型的实例化,编译器会推导.front.
2)如果用户在不抛代码中使用你代码,那么模板不应引入无法编译.为此,应用适当属性的单元测试来确保,如,当Foo非抛类型实例化时,它不会引入.
你当然可以.纯 单元测试代码显然自身必须是纯的(否则不编译).如果Foo引入了不纯,那么作为的单元测试禁止调用Foo不纯方法,这正是想要的.有什么问题?

这很简单:编写单元测试,用故意不纯的类型实例化Foo(如,.front引用聚集外部单元测试中的一些局部变量).如果Foo中有免费的pure,这不会编译.

你思考方式错了.
使用模板函数时加上pure,你是在说"仅允许可为纯的函数实例化".本质上,就是告诉用户"它必须是的!".

如果意图是仅强制纯函数,就是你这样.如果意图是确保给定正确参数,函数将是的,那么答案是单元测试.
有时这真的很烦人.就像单元测试失败一样,你得到的为什么不工作的信息很少.
即,期望推导的,但不是.你得到的只是"不纯的单元测试不能调用不纯foo(...)"函数.今天很难找出错误推导原因.我希望它会更容易.

+1,需要更好的诊断.
这样?:每当编译器推导某个F的函数属性时,连同推导属性,它还会附加排除属性在推导之外的位置列表.如,如果推导F不纯,编译器还保存F中不纯操作的第一行位置.每当纯代码调用F时,编译器打印出此引用(文件+行+列)及错误消息.即,“G纯函数不能调用F不纯函数,因为不纯是从F函数中的第1234行推导出来的.”
显然,这仅适合具有推导属性的函数;如果属性在代码中是显式的,那么就无可说的了.由于具有推导属性的函数必须始终可访问其主体(否则无法推导),因此可以保证始终可以找到上述引用.
编译器确实应该给这些信息,而不是让自己弄清楚

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