使用模板指定一个函数,该函数接受和 returns 任意 class

Specifying a function with templates that takes and returns an arbitrary class

我有兴趣定义一个函数,给定一个 class 变量,生成一个 class 对象的新实例,其中随机选择的成员属性发生变异。

上下文:考虑一些 class、Circle 的实例 circle1,具有属性 colorradius。这些属性的赋值分别为 red5。有问题的函数 mutate 必须接受 circle1 作为参数,但拒绝非 class 参数。

对于其他数据类型,模板在此上下文中提供了答案。也就是说,模板可用于指定可以接受多种类型参数的函数的通用实例。

如何使用模板定义接受(和returns)任何class实例的通用函数?

一般来说,如果您需要限制模板可以采用的内容,您可以使用模板约束。例如

import std.traits : isIntegral;

auto foo(T)(T t)
    if(isIntegeral!T)
{
    ...
}

import std.functional : binaryFun;

auto foo(alias pred, T, U)(T t, U u)
    if(is(typeof(binaryFun!pred(t, u.bar())) == bool)
{
    ...
}

只要条件可以在编译时检查,你几乎可以测试任何东西。它也可以用于函数重载(例如 std.algorithm.searching.find has quite a few overloads all of which are differentiated by template constraint). The built-in __traits, the eponymous templates in std.traits, and is expressions provide quite a few tools for testing stuff at compile time and then using that information in template constraints or static if 条件。

如果您特别想测试某物是否为 class,则使用带有 == classis 表达式。例如

auto foo(T)(T t)
    if(is(T == class))
{
    ...
}

不过一般来说,您可能希望使用更具体的条件,例如 __traits(compiles, MyType result = t.foo(22))is(typeof(t.foo(22)) == MyType)。所以,你可以有类似

的东西
auto mutate(T)(T t)
    if(is(T == class) &&
       __traits(compiles, t.color = red) &&
       __traits(compiles, t.radius = 5))
{
    ...
}

如果条件是您想要重复使用的东西,那么创建一个同名模板是有意义的 - 这就是在 Phobos 中所做的事情,例如 std.range.primitives and std.range.traits. For instance, to test for an input range, std.range.primitives.isInputRange 看起来像

template isInputRange(R)
{
    enum bool isInputRange = is(typeof(
    {
        R r = R.init;     // can define a range object
        if (r.empty) {}   // can test for empty
        r.popFront();     // can invoke popFront()
        auto h = r.front; // can get the front of the range
    }));
}

然后需要输入范围的代码可以使用它。所以,Phobos 中的很多函数都有类似

的东西
auto foo(R)(R range)
    if(isInputRange!R)
{
    ...
}

一个更具体的例子是 find:

的重载
InputRange find(alias pred = "a == b", InputRange, Element)
               (InputRange haystack, Element needle)
    if(isInputRange!InputRange &&
       is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
{
    ...
}

Ali Çehreli 的书 Programming in D 有几个相关章节,包括:

http://ddili.org/ders/d.en/templates.html
http://ddili.org/ders/d.en/cond_comp.html
http://ddili.org/ders/d.en/is_expr.html
http://ddili.org/ders/d.en/templates_more.html