现在的位置: 首页 > 综合 > 正文

GCC-3.4.6源代码学习笔记(147-续1)

2013年02月22日 ⁄ 综合 ⁄ 共 8985字 ⁄ 字号 评论关闭

然后如果一切顺利,最后通过
10401

fn_type_unifcation


把推导后实参放入
targs

。而这些实参接着在
resolve_overloaded_unification



9334
行,立即由
tsubst


替代入模板形参。

接着在下面的函数中,
tparms

指向模板形参;
orig_targs

是推导出的实参的
vector
;两者都是调用者
resolve_overloaded_unification


的参数
tparms


targs

,并且在下面的过程中都不会改变。而
targs


orig_tags

的拷贝;
parm

是模板形参;
arg

则是相应的模板实参(在这里它们是函数指针类型);而
addr_p

如果是
true
,表示看到“
&
”。

 

9380

static
int

9381

try_one_overload

(tree tparms,                                                                         
in
pt.c

9382

               
tree
orig_targs,

9383

               
tree targs,

9384

               
tree parm,

9385

               
tree arg,

9386

      
         
unification_kind_t strict,

9387

               
int sub_strict,

9388

               
bool addr_p)

9389

{

9390

  
int nargs;

9391

  
tree tempargs;

9392

  
int i;

9393

9394

  
/*
[temp.deduct.type] A template-argument can be deduced from a pointer

9395

 
   
to function or pointer to member function
argument if the set of

9396

    
overloaded functions does not contain
function templates and at most

9397

    
one of
a set of overloaded functions provides a unique match.

9398

9399

    
So if
this is a template, just return success. 

*/

9400

9401

  
if (uses_template_parms
(arg))

9402

    
return
1;

9403

9404

  
if (TREE_CODE (arg) ==
METHOD_TYPE)

9405

    
arg =
build_ptrmemfunc_type (build_pointer_type
(arg));

9406

  
else if (addr_p)

9407

    
arg = build_pointer_type
(arg);

9408

9409

  
sub_strict |= maybe_adjust_types_for_deduction
(strict, &parm, &arg);

 

当使用指向函数指针(
pointer-to-function
)或指向方法指针(
pointer-to-method
)的模板形参时,我们可以仅使用“
&A::f
”或“
&f
”(对于函数,还可以使用“
f
”)及
typedef
别名作为实参,因为前端要求编译时的常量。因此上面,在看到这样的结构后,构建相应的指针。


3
】的
[temp.deduct.conv]
定义到“模板实参推导,通过模板转换函数的返回类型
(称之为


P

)与作为转换结果所要求的类型(称之为
A

)的比较,如
14.8.2
.4

所示,来完成。


[temp.deduct.call]
提到:“模板实参推导,通过所下所示的,比较每个函数模板形参类型
(称之为


P

)与调用的对应实参类型(称之为
A

),来完成

”。

记住对于转换操作符,已经把该操作符的返回类型加入到了形参列表,被期望的类型加入到了实参列表。并且看到在该操作符的调用点,它将尝试把返回类型转换为被期望的类型(即从形参到实参,而不是像函数参数那样从实参到形参)。因此下面的函数首先通过
DEDUCE_CONV
交换了这两者(现在,对于转换操作符的规则,
P
变成了
A

A
变成了
P
)。进一步的,【
3
】有如下定义:

来自
[temp.deduct.call]


2. 


如果
P
不是一个引用类型:


如果
A
是一个数组类型,由数组到指针标准转换(
4.2
)产生的指针类型,在类型推导中,取代
A
;否则,


如果
A
是一个函数类型,由函数到指针标准转换(
4.3
)产生的指针类型,在类型推导中,取代
A
;否则,


如果
A
是一个
cv-
限定的类型,对于类型推导,
A
的类型的顶层
cv-
限定词被忽略。

如果
P
是一个
cv-
限定的类型,对于类型推导,
P
的类型的顶层
cv-
限定词被忽略。如果
P
是一个引用类型,在类型推导中,为
P
所引用的类型被使用。

来自
[temp.deduct.conv]


2. 

如果
A
不是一个引用类型:


如果
P
是一个数组类型,由数组到指针标准转换(
4.2
)产生的指针类型,在类型推导中,取代
P
;否则,


如果
P
是一个函数类型,由函数到指针标准转换(
4.3
)产生的指针类型,在类型推导中,取代
P
;否则,


如果
P
是一个
cv-
限定的类型,对于类型推导,
P
的类型的顶层
cv-
限定词被忽略。

如果
A
是一个
cv-
限定的类型,对于类型推导,
A
的类型的顶层
cv-
限定词被忽略。如果
A
是一个引用类型,在类型推导中,为
A
所引用的类型被使用。

来自
[temp.deduct.call]


3. 

通常,推导处理尝试找出将使得推导后的
A

A
(在如上所述的转换后的类型
A
)相同的模板实参值。不过,有三种情况允许不同:


如果初始的
P
是一个引用类型,推导的
A
(即,由该引用引用的类型)可以是比
A
更严格的
cv-
限定。

— A
可以是另一个指针或指向成员指针类型,它可以通过一个限定转换(
4.4
)转换到推导的
A


如果
P
是一个类,并且
P
具有
template-id
的形式,那么
A
可以是推导的
A
的一个派生类。同样,如果
P
是一个具有
template-id
形式的类的指针,
A
可以是推导的
A
所指向类的一个派生类的指针。

仅当类型推导没有其它选择,才考虑这些替代方案。如果它们产生多于一个可能的推导的
A
,类型推导失败。
[
注意:如果一个模板形参在一个函数模板的任一一个函数形参中都没有使用,或者仅用在一个非推导(
non-deduced
)的上下文,其对应的模板实参不能从一个函数调用中推导,这个模板实参必须被显式指定
]

来自
[temp.deduct.conv]


3. 

通常,推导处理尝试找出将使得推导后的
A

A
相同的模板实参值。不过,有两种情况允许不同:


如果初始的
A
是一个引用类型,
A
可以是比推导的
A
(即,由该引用引用的类型)更严格的
cv-
限定。


推导的
A
可以是另一个指针或指向成员指针类型,它可以通过一个限定转换转换到
A

仅当类型推导没有其它选择,才考虑这些替代方案。如果它们产生多于一个可能的推导的
A
,类型推导失败。

看到通过交换
arg


parm

,可以把
[temp.deduct.call]
的第二个规则同时用于函数调用及转换操作符,而不需作出改变。而对于
[temp.deduct.call]

[temp.deduct.conv]
的第三个规则,观察下面的例子:


1

template
<class
T> void func (const
T&);          
// const T& is P

int a;

main () { func (a); }             
// const int is deduced A, and A is int


2

template
<typename
T> class
A {

public
:

T i;

operator
T& () { return
i; }

// operator const T& () { return i; } // const
int& is P (A after swap)

in
A<int> a

};

void func (const
int&) {}             
// const
int& is A (P after swap)

// void func (int&) {}    
// int& is A (P after swap)

main () {

 
A<int> a;    
// int& is P
(A after swap)

func (a);     
// int is deduced A, const int is deduced A for commented
out operator

}

如果恢复上面注掉的语句,将导致
cv-
限定冲突的错误。可以看到对于转换操作符,通过交换
parm


arg

,我们可以安全地为函数调用及转换操作符,统一使用推导函数调用的规则
3
中的前两个替代方案。而对于函数调用规则
3
中的第三个替代方案,前端可以通过不设定标记
UNIFY_ALLOW_DERIVED
,来为转换操作符避免它。

 

9007

static
int

9008

maybe_adjust_types_for_deduction

(unification_kind_t strict,                       
      
in pt.c

9009

                              
tree* parm,

9010

                              
tree* arg)

9011

{

9012

  
int result = 0;

9013

  

9014

  
switch
(strict)

9015

  
{

9016

    
case
DEDUCE_CALL:

9017

      
break
;

9018

9019

    
case
DEDUCE_CONV:

9020

 
   
{

9021

      
/*
Swap PARM and ARG throughout the remainder of this

9022

        
function; the handling is precisely
symmetric since PARM

9023

        
will initialize ARG rather than vice
versa. 
*/

9024

      
tree* temp = parm;

9025

      
parm = arg;

9026

      
arg = temp;

9027

      
break
;

9028

    
}

9029

9030

    
case
DEDUCE_EXACT:

9031

      
/*
There is nothing to do in this case. 
*/

9032

      
return
0;

9033

9034

    
case
DEDUCE_ORDER:

9035

      
/*
DR 214. [temp.func.order] is underspecified, and leads to no

9036

        
ordering between things like `T *' and
`T const &' for `U *'.

9037

        
The former has T=U and the latter T=U*.
The former looks more

9038

        
specialized and John Spicer considers
it well-formed (the EDG

9039

        
compiler accepts it).

9040

9041

        
John also confirms that deduction
should proceed as in a function

9042

        
call. Which implies the usual ARG and
PARM conversions as DEDUCE_CALL.

9043

        
However, in ordering, ARG can have
REFERENCE_TYPE, but no argument

9044

        
to
an actual call can have such a type.

9045

         

9046

        
If
both ARG and PARM are REFERENCE_TYPE, we change neither.

9047

        
If
only ARG is a REFERENCE_TYPE, we look through that and then

9048

        
proceed as with DEDUCE_CALL (which
could further convert it). 
*/

9049

      
if (TREE_CODE (*arg) ==
REFERENCE_TYPE)

9050

      
{

9051

        
if (TREE_CODE (*parm)
== REFERENCE_TYPE)

9052

          
return
0;

9053

        
*arg = TREE_TYPE
(*arg);

9054

      
}

9055

      
break
;

9056

    
default
:

9057

      
abort ();

9058

  
}

9059

9060

  
if (TREE_CODE (*parm) !=
REFERENCE_TYPE)

9061

  
{

9062

   
 
/* [temp.deduct.call]

9063

  

9064

      
If P
is not a reference type:

9065

  

9066

      
--If
A is an array type, the pointer type produced by the

9067

       
array-to-pointer standard conversion
(_conv.array_) is

9068

       
used in place of A for type deduction;
otherwise,

9069

  

9070

      
--If
A is a function type, the pointer type produced by

9071

       
the
function-to-pointer standard conversion

9072

       
(_conv.func_) is used in place of A for
type deduction;

9073

       
otherwise,

9074

  

9075

      
--If
A is a cv-qualified type, the top level

9076

       
cv-qualifiers of A's type are ignored
for type

9077

       
deduction. 
*/

9078

    
if (TREE_CODE (*arg) ==
ARRAY_TYPE)

9079

      
*arg = build_pointer_type
(TREE_TYPE (*arg));

9080

    
else if (TREE_CODE (*arg)
== FUNCTION_TYPE)

9081

      
*arg = build_pointer_type
(*arg);

9082

    
else

9083

      
*arg = TYPE_MAIN_VARIANT
(*arg);

9084

  
}

9085

  

9086

  
/*
[temp.deduct.call]

9087

     

9088

    
If P
is a cv-qualified type, the top level cv-qualifiers

9089

    
of P's
type are ignored for type deduction. If P is a

9090

    
reference type, the type referred to by P
is used for

9091

    
type
deduction. 
*/

9092

  
*parm = TYPE_MAIN_VARIANT
(*parm);

9093

  
if (TREE_CODE (*parm) ==
REFERENCE_TYPE)

9094

  
{

9095

    
*parm = TREE_TYPE (*parm);

9096

    
result |=
UNIFY_ALLOW_OUTER_MORE_CV_QUAL;

9097

  
}

9098

9099

 
 
/* DR 322. For conversion deduction, remove a
reference type on parm

9100

    
too
(which has been swapped into ARG). 
*/

9101

  
if (strict == DEDUCE_CONV
&& TREE_CODE (*arg) == REFERENCE_TYPE)

9102

   
 
*arg = TREE_TYPE (*arg);

9103

  

9104

  
return
result;

9105

}

 

那么函数
maybe_adjust_types_for_deduction


根据上面表中由【
3
】指定规则转换实参。
9035
行的注释提及了【
3
】中的一个瑕疵,并给出了
EDG
(最好的
C++
前端)所采纳的一个可能的解决方案。

 

try_one_overload (continue)

 

9411

  
/*
We don't copy orig_targs for this because if we have already deduced

9412

    
some
template args from previous args, unify would complain when we

9413

    
try to
deduce a template parameter for the same argument, even though

9414

    
there
isn't really a conflict. 
*/

9415

  
nargs = TREE_VEC_LENGTH (targs);

9416

  
tempargs = make_tree_vec
(nargs);

9417

9418

  
if (unify
(tparms, tempargs, parm, arg, sub_strict) != 0)

9419

    
return
0;

 


3
】的条文
14.8.2
.4
“从一个类型推导模板实参”如下所示
[temp.deduct.type
]



1. 

模板实参可以在几个不同的上下文中推导,但在每个情形中,以模板形参指定的一个类型(称之为
P
)被与一个实际上的类型(称之为
A
)比较,并且尝试找出模板实参值(对于一个类型参数是一个类型,对于非类型参数是一个值,或者对于模板参数是一个模板),它将使得
P
,在进行推导值的替换后(称之为推导的
A
),与
A
相容。

2. 

在某些情况下,推导使用单组类型
P

A
完成。在其他情况下,将有一组对应的类型
P

A
。对于每对
P/A
,类型推导独立完成,然后推导的模板实参值被组合起来。如果对任一
P/A
对,类型推导不能完成;或者如果对于任一对,导致推导出多于一组可能的推导值;或者如果不同的对产生不同的推导值;或者如果任一模板实参保持未推导,及未被显式指定;模板实参推导失败。

3. 

一个给定的类型
P
可以由一定数目的其他类型,模板,及非类型值组成:


一个函数类型包括每个函数参数的类型及返回类型。


一个指向成员类型的指针包括所指向类对象的类型,及所指向成员的类型。


一个类模板特化(如,
A<int>
)的一个类型包括该特化实参列表所引用的类型,模板,及非类型值。


一个数组类型包括数组元素的类型,及数组边界值。

在大多数情况下,构成
P
的类型,模板,非类型值参予模板实参推导。也就是说,它们可能被用于确定一个模板实参的值,并且这里确定的值必须与其他地方确定的值一致。然而,在某些上下文中,该值不参予类型推导,而是使用在其他地方推导的,或者显式指定的模板实参的值。如果一个模板实参仅用在非推导上下文,并且没有被显式指定,模板实参推导失败。

4. 


非推导上下文是:


使用一个
qualified-id
所指定的一个
nested-name-specifier
形式的
type

—一个
template-id
形式的类型,其中一个或多个模板实参是一个引用模板形参的表达式。

当一个类型的名字在以一个包含非推导上下文的方式指定时,所有组成这个类型名的类型也都是非推导的。不过,一个复合类型(
compound type
)可以同时包括推导及非推导类型。
[
例如:如果一个类型被指定为
A<T>::B<T2>

T

T2
两者都是非推导的。类似的,如果一个类型被指定为
A<I+J>::X<T>

I

J
,及
T
都是非推导的。如果一个类型被指定为
void f(typename
A<T>::B, A<T>)

,在
A<T>::B
中的
T
是非推导的,但在
A<T>
中的
T
是推导的
]

5. 
[

例如:这里有一个例子,在其中不同的形参
/
实参对产生不一致的模板实参推导:

template
<class
T>
void f(T x, T y) { /* ...

*/
}

struct
A { /* ... */
};

struct
B : A { /* ... */
};

int g(A a, B b) {

f(a,b); // error: T could be A or B

f(b,a); // error: T could be A or B

f(a,a); // OK: T is A

f(b,b); // OK: T is B

}

6. 

这里有一个例子,其中的
2
个模板实参从单个函数形参
/
实参对推导出来。这会导致冲突使得类型推导失败:

template
<class
T, class
U> void f(T (*) (T, U, U) );

int g1( int, float, float);

char g2( int, float, float);

int g3( int, char, float);

void r() {

f(g1); // OK: T is int and U is float

f(g2); // error: T could be char or int

f(g3); // error: U could be char or
float

}

7. 

这里有一个例子,其中在函数调用的实参类型与推导的模板实参类型之间,应用了一个限定转换:

template
<class
T>
void f(const
T*) {}

int *p;

void s() {

f(p); // f(const int *)

}

8. 

这里有一个例子,其中的模板实参用于具现对应函数形参类型的一个派生类:

template
<class
T> stru

抱歉!评论已关闭.