我有兴趣定义一个给定类变量的函数,生成一个具有随机选择的成员属性的类对象的新实例。
上下文:考虑某个类circle1
的实例Circle
,其属性为color
和radius
。这些属性分别指定为red
和5
的值。有问题的函数mutate
必须接受circle1
作为参数,但拒绝非类参数。
对于其他数据类型,模板在此上下文中提供答案。也就是说,模板可用于指定可接受多种类型参数的函数的通用实例。
如何使用模板定义接受(并返回)任何类实例的泛型函数?
答案 0 :(得分:2)
通常,如果您需要限制模板可以采用的内容,则使用模板约束。 e.g。
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
具有相当多的重载,所有这些重载都通过模板约束来区分)。内置的__traits
,std.traits
中的同名模板和is
expressions提供了相当多的工具,用于在编译时测试内容,然后在模板约束中使用该信息或{{3}条件。
如果您特别想测试某些内容是否为某个类,请使用is
表达式== class
。 e.g。
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在static if
和std.range.primitives等地方所做的事情。例如,要测试输入范围,std.range.traits看起来像
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)
{
...
}
一个更具体的例子就是std.range.primitives.isInputRange
的重载:
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的书find
,有几个相关的章节,包括:
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