struct S(int a, int b) { }
void fun(T)(T t) { }
我希望fun
仅与S
合作。签名约束会是什么样的?
我无法让fun
成为S
的成员,而void fun(T)(T t) if(is(T : S)) { }
我会Error: struct t1.S(int a,int b) is used as a type
答案 0 :(得分:10)
S
不是一种类型。它是一种类型的模板。 S!(5, 4)
是一种类型。 S
的不同实例化很可能会生成完全不同的代码,因此S!(5, 4)
的定义可能完全与S!(2, 5)
不同。例如,S
可能是
struct S(int a, int b)
{
static if(a > 3)
string foo;
static if(b == 4)
int boz = 17;
else
float boz = 2.1;
}
请注意,成员变量的数量和类型不同,因此您无法真正使用S!(5, 4)
代替S!(2, 5)
。它们也可能是名为U
和V
的结构体,它们根本没有模板化它们彼此之间的所有关系。
现在,特定模板的不同实例化关于它们的API通常是相似的(或者它们可能不会使用相同的模板完成),但从编译器的角度来看,它们彼此之间没有关系。因此,处理它的常用方法是纯粹使用类型的API而不是其名称或实例化的模板。
因此,如果您希望S
拥有foo
,bar
和foozle
函数,并且您希望fun
使用这些函数,那么你将构造一个约束,测试给fun
的类型具有那些函数并且它们按预期工作。例如
void fun(T)(T t)
if(is({ auto a = t.foo(); t.bar(a); int i = t.foozle("hello", 22);}))
{}
然后任何类型都有一个名为foo
的函数返回一个值,一个名为bar
的函数可能会返回一个值,也可能不会返回一个值foo
的结果,以及一个名为foozle
的函数接受string
和int
并返回int
将使用fun
进行编译。因此,fun
比仅坚持使用S
的实例化要灵活得多。在大多数情况下,这些约束被分离为单独的同名模板(例如isForwardRange
或isDynamicArray
),而不是将原始代码放在is expression
中,以便它们可以重用(并且更加用户友好) ),但这样的表达式就是这种同名模板在内部使用的。
现在,如果确实坚持限制fun
,使其仅适用于S
的实例化,那么我知道有两个选项。< / p>
1。添加S
始终拥有的某种声明,并且您不希望任何其他类型的声明。例如
struct S(int a, int b)
{
enum isS = true;
}
void fun(T)(T t)
if(is(typeof(T.isS)))
{}
请注意,声明的实际值无关紧要(也不是其类型)。这是一个简单的事实,它存在你正在测试。
2。更优雅(但不那么明显的解决方案)就是这样做:
struct S(int a, int b)
{
}
void fun(T)(T t)
if(is(T u : S!(i, j), int i, int j))
{}
一旦变得非常复杂, is
expressions就会对伏都教有一定的倾向,但带有逗号的版本正是你所需要的。 T u
是您正在测试的类型和标识符; : S!(i, j)
给出了您希望T
成为实例化的模板特化;其余的是TemplateParameterList
,声明左边的东西中使用的符号,但之前没有声明过 - 在这种情况下,i
和j
。
答案 1 :(得分:7)
我认为在其他答案中有一些小红色鲱鱼。您可以使用模式匹配来确定T是否是S的某个实例,如下所示。
最简单的方法是对参数本身进行模式匹配:
void fun(int a, int b)(S!(a, b) t) {
}
更一般地说,你可以在模板约束中的分离中进行模式匹配:
void fun(T)(T t) if (is(T U == S!(a, b), int a, int b)) {
}
在这两种情况下,您都可以访问实例化参数。
答案 2 :(得分:3)
“只使用S
”在D中没有意义,因为
S
不是类型,而是模板。
与其他语言不同,D中的模板本身“某事物”。
你写的是简写:
template S(int a, int b) { struct S { } }
因此类型的全名为S(a, b).S
,适用于您使用的a
或b
。没有办法让它“一般地”引用S
。
如果您需要设置这样的约束,我建议在S
中放置一些私有内容,并检查T
是否具有相同的成员。