• 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

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

    @nogc nothrow pure @safe
    unittest
    {
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    static assert(!__traits(compiles, () pure {
        // 代码
    });
    
    • 1
    • 2
    • 3

    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行推导出来的.”
    显然,这仅适合具有推导属性的函数;如果属性在代码中是显式的,那么就无可说的了.由于具有推导属性的函数必须始终可访问其主体(否则无法推导),因此可以保证始终可以找到上述引用.
    编译器确实应该给这些信息,而不是让自己弄清楚

  • 相关阅读:
    Python常用基础语法知识点大全合集,看完这一篇文章就够了
    手动编译与安装Qt的子模块
    IOS企业IPA软件证书 苹果签名证书 有效期到2026年
    2022年06月 Scratch(一级)真题解析#中国电子学会#全国青少年软件编程等级考试
    Kafka生产与消费示例
    工厂模式和单例模式
    HCS 中的一些概念(二)
    前端面试题【js动态创建节点、怎么阻止冒泡事件、怎么阻止默认事件、什么是深拷贝,什么是浅拷贝、js造成内存泄漏的操作有哪些等】
    windows音频服务未响应,电脑装完驱动还是软件导致没有声音
    Java中的final关键字,你清楚吗?
  • 原文地址:https://blog.csdn.net/fqbqrr/article/details/126829275