码迷,mamicode.com
首页 > 编程语言 > 详细

C++中模板特殊化(speicialization)的偏序关系及make规则选择

时间:2020-01-18 12:22:23      阅读:67      评论:0      收藏:0      [点我收藏+]

标签:cto   value   逻辑   sign   har   like   any   manual   hellip   

一、Makefile中的匹配

在Makefile中,可以通过不同的pattern指定不同目标的执行规则,在这个时候就会存在一个哪个target更合适的问题。这里Makefile中引入的是一个stem的概念,从stem中选择最短的。
gnu make对该规则的说明
A target pattern is composed of a ‘%’ between a prefix and a suffix, either or both of which may be empty. The pattern matches a file name only if the file name starts with the prefix and ends with the suffix, without overlap. The text between the prefix and the suffix is called the stem. Thus, when the pattern ‘%.o’ matches the file name test.o, the stem is ‘test’. The pattern rule prerequisites are turned into actual file names by substituting the stem for the character ‘%’. Thus, if in the same example one of the prerequisites is written as ‘%.c’, it expands to ‘test.c’.
……
It is possible that more than one pattern rule will meet these criteria. In that case, make will choose the rule with the shortest stem (that is, the pattern that matches most specifically). If more than one pattern rule has the shortest stem, make will choose the first one found in the makefile.
……
If make is asked to build lib/bar.o and both lib/bar.c and lib/bar.f exist, then the third rule will be chosen since the stem for this rule (‘bar’) is shorter than the stem for the first rule (‘lib/bar’). If lib/bar.c does not exist then the third rule is not eligible and the second rule will be used, even though the stem is longer.
make-3.82\implicit.c
int
stemlen_compare (const void *v1, const void *v2)
{
const struct tryrule *r1 = v1;
const struct tryrule *r2 = v2;
int r = r1->stemlen - r2->stemlen;
return r != 0 ? r : (int)(r1->order - r2->order);
}
static int
pattern_search (struct file *file, int archive,
unsigned int depth, unsigned int recursions)
{
……
/* Sort the rules to place matches with the shortest stem first. This
way the most specific rules will be tried first. */
if (nrules > 1)
qsort (tryrules, nrules, sizeof (struct tryrule), stemlen_compare);
……
}

二、C++中多模板匹配原则

在C++的模板选择中,其实面临的问题和使用的策略本质上也是和这个类似,只是这里匹配的不是字符而是一个类型的组成元素。但是,正如字符组成了句子,每个类型元素在一起组成了类型。一些人眼看起来比较直观的事情,对于计算机这种只理解数值类型的机器来说,不能通过“直观”来说明,而必须要通过“算法”或者“数值”来确定优先级。

下面是C++中对该问题的“算法描述
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process. If both deductions succeed, the partial ordering selects the more constrained template as described by the rules in [temp.constr.order].
这里的说明,直观上理解,比较两个模板specialization,可以把第一个模板的所有模板声明看做一个具体类型来匹配另一个specialization:如果A可以满足B的要求,但反过来不行,则认为B匹配的更好。
再通俗的说,就是把A模板中的类型认为是一个具体的类型,例如int类型,来尝试匹配另一个模板,看是否可以互换。当然,这里有一个前提:A和B都是满足可以使用原始实例化现场提供的类型参数的。

三、看一个例子

tsecer@harry: cat -n template.cpp
1 #include <stdio.h>
2
3 template<typename T>
4 struct S
5 {
6
7 void foo() {printf("%s %d \n", __func__, __LINE__);}
8 };
9
10 template<typename T>
11 struct S<const T>
12 {
13
14 void foo() {printf("%s %d \n", __func__, __LINE__);}
15 };
16
17 template<typename T>
18 struct S<T *>
19 {
20
21 void foo() {printf("%s %d \n", __func__, __LINE__);}
22 };
23
24
25
26 int main(int argc, const char * argv[])
27 {
28 S<int * const> s;
29 S<int > ss;
30 S<int *> sss;
31 S<const int *> ssss;
32
33 s.foo();
34 ss.foo();
35 sss.foo();
36 ssss.foo();
37 }
tsecer@harry: g++ template.cpp
tsecer@harry: ./a.out
foo 14
foo 7
foo 21
foo 21
tsecer@harry:
以S<int * const>实例化为例,struct S<const T>和t主声明S<T>都匹配,所以此时需要在两者之间选择一个更特殊化的。
1、把S<const T>中的T看做是int,使用const int来匹配 S<T>此时是可以满足匹配的。
2、把S<T>中的T看作是int,使用int来匹配S<const T>无法完成匹配。
所以S<const T>比S<T>更加特殊化。这里把T看做是int只是为了便于理解,在编译器看来,这个用来进行匹配的是有自己的类型的,只是在匹配逻辑上看,这种类型和int一样。当我们人肉执行这个匹配的时候,认为是一个int更加直观罢了。

四、gcc中对于该问题的处理代码

从下面more_specialized_class函数可以看到,get_class_bindings把一个模板作为另一个模板的参数输入尝试进行匹配。
gcc-4.9.0\gcc\cp\pt.c

/* Determine which of two partial specializations of TMPL is more
specialized.

PAT1 is a TREE_LIST whose TREE_TYPE is the _TYPE node corresponding
to the first partial specialization. The TREE_VALUE is the
innermost set of template parameters for the partial
specialization. PAT2 is similar, but for the second template.

Return 1 if the first partial specialization is more specialized;
-1 if the second is more specialized; 0 if neither is more
specialized.

See [temp.class.order] for information about determining which of
two templates is more specialized. */

static int
more_specialized_class (tree tmpl, tree pat1, tree pat2)
{
tree targs;
tree tmpl1, tmpl2;
int winner = 0;
bool any_deductions = false;

tmpl1 = TREE_TYPE (pat1);
tmpl2 = TREE_TYPE (pat2);

/* Just like what happens for functions, if we are ordering between
different class template specializations, we may encounter dependent
types in the arguments, and we need our dependency check functions
to behave correctly. */
++processing_template_decl;
targs = get_class_bindings (tmpl, TREE_VALUE (pat1),
CLASSTYPE_TI_ARGS (tmpl1),
CLASSTYPE_TI_ARGS (tmpl2));
if (targs)
{
--winner;
any_deductions = true;
}

targs = get_class_bindings (tmpl, TREE_VALUE (pat2),
CLASSTYPE_TI_ARGS (tmpl2),
CLASSTYPE_TI_ARGS (tmpl1));
if (targs)
{
++winner;
any_deductions = true;
}
--processing_template_decl;

/* In the case of a tie where at least one of the class templates
has a parameter pack at the end, the template with the most
non-packed parameters wins. */
if (winner == 0
&& any_deductions
&& (template_args_variadic_p (TREE_PURPOSE (pat1))
|| template_args_variadic_p (TREE_PURPOSE (pat2))))
{
tree args1 = INNERMOST_TEMPLATE_ARGS (TREE_PURPOSE (pat1));
tree args2 = INNERMOST_TEMPLATE_ARGS (TREE_PURPOSE (pat2));
int len1 = TREE_VEC_LENGTH (args1);
int len2 = TREE_VEC_LENGTH (args2);

/* We don‘t count the pack expansion at the end. */
if (template_args_variadic_p (TREE_PURPOSE (pat1)))
--len1;
if (template_args_variadic_p (TREE_PURPOSE (pat2)))
--len2;

if (len1 > len2)
return 1;
else if (len1 < len2)
return -1;
}

return winner;
}

/* Return the innermost template arguments that, when applied to a partial
specialization of TMPL whose innermost template parameters are
TPARMS, and whose specialization arguments are SPEC_ARGS, yield the
ARGS.

For example, suppose we have:

template <class T, class U> struct S {};
template <class T> struct S<T*, int> {};

Then, suppose we want to get `S<double*, int>‘. The TPARMS will be
{T}, the SPEC_ARGS will be {T*, int} and the ARGS will be {double*,
int}. The resulting vector will be {double}, indicating that `T‘
is bound to `double‘. */

static tree
get_class_bindings (tree tmpl, tree tparms, tree spec_args, tree args)
{
……
}

五、模板匹配和类型匹配的类似性

和字符匹配一样,类型匹配也是有严格顺序要求的,并且必须按顺序匹配。例如,对于前面的例子
struct S<T *>为什么不能匹配S<int * const> s;呢?
因为s的具体类型首先是 const,然后是指针,最后是int。而T *的第一个类型是指针,所以无法匹配成功。

六、模板参数作为参数匹配另一个时的编译器类型

前面说明把T当做int来比较两个模板哪个更特殊,这个只是直观上的理解。其实在编译器内部进行匹配时,这个地方的类型是TEMPLATE_TEMPLATE_PARM类型,这种类型在模板匹配中,和int一样是一个原子类型,所以看做int这种我们最常见的类型比较直观。
/* Process information from new template parameter PARM and append it
to the LIST being built. This new parameter is a non-type
parameter iff IS_NON_TYPE is true. This new parameter is a
parameter pack iff IS_PARAMETER_PACK is true. The location of PARM
is in PARM_LOC. */

tree
process_template_parm (tree list, location_t parm_loc, tree parm,
bool is_non_type, bool is_parameter_pack)
{
……
t = cxx_make_type (TEMPLATE_TYPE_PARM);
/* parm is either IDENTIFIER_NODE or NULL_TREE. */
decl = build_decl (parm_loc,
TYPE_DECL, parm, t);
……
}

C++中模板特殊化(speicialization)的偏序关系及make规则选择

标签:cto   value   逻辑   sign   har   like   any   manual   hellip   

原文地址:https://www.cnblogs.com/tsecer/p/12208585.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!