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

原创 2011年01月22日 08:38:00

然后如果一切顺利,最后通过 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> struct B { };

template <class T> struct D : public B<T> {};

struct D2 : public B<int> {};

template <class T> void f(B<T>&){}

void t() {

D<int> d;

D2 d2;

f(d); // calls f(B<int>&)

f(d2); // calls f(B<int>&)

}

—例子结束 ]

9.  一个模板类型实参 T ,一个模板模板实参 TT ,或一个模板非类型实参 i 可以被推导,如果 P A 具有如下的形式:

T

cv-list T

T*

T&

T[integer-constant]

template-name<T> (其中 template-name 引用一个类模板)

type(*)(T)

T(*)()

T(*)(T)

T type::*

type T::*

T T::*

T (type::*)()

type (T::*)()

type (type::*)(T)

type (T::*)(T)

T (type::*)(T)

T (T::*)()

T (T::*)(T)

type[i]

template-name<i> (其中 template-name 引用一个类模板)

TT<T>

TT<i>

TT<>

其中 (T) 代表至少一个实参类型包含一个 T 的实参列表;而 () 代表没有参数包含一个 T 的实参列表。同样, <T> 代表至少一个实参包含一个 T 的模板实参列表, <i> 代表至少一个实参包含一个 i 的模板实参列表,而 <> 代表没有实参包含一个 T i 的模板实参列表。

10. 这些形式可以以相同的方式来使用,因为 T 可用于进一步的类型合成。 [ 例如:

X<int> (*)(char[6])

具有形式

template-name<T> (*)(type[i])

它是下面的一个变形

type (*)(T)

其中 type X<int> T char[6] ]

11. 模板实参不能从涉及上述以外结构的函数实参中推导。

12. 一个模板类型实参不能根据一个非类型模板实参的类型来推导。 [ 例如:

template <class T, T i> void f(double a[10][i]);

int v[10][20];

f(v); // error: argument for template-parameter T cannot be deduced

—例子结束 ]

13. [ 注意:除了作为引用及指针类型,一个主要数组边界不能是一个函数形参类型的一部分,并且不能从一个实参推导出来:

template <int i> void f1(int a[10][i]);

template <int i> void f2(int a[i][20]);

template <int i> void f3(int (&a)[i][20]);

void g() {

int v[10][20];

f1(v); // OK: i deduced to be 20

f1<20>(v); // OK

f2(v); // error: cannot deduce template-argument i

f2<10>(v); // OK

f3(v); // OK: i deduced to be 10

}

14. 如果,在一个带有一个非类型模板参数的函数模板的声明中,该非类型模板形参被用于该函数形参列表中的一个表达式里,对应的模板实参必须总是显式指定或从其他地方推导出来;不然的话,对于这样的一个模板实参,类型推导将失败。

template <int i> class A { /* ... */ };

template <short s> void g(A<s+1>);

void k() {

A<1> a;

g(a); // error: deduction fails for expression s+1

g<0>(a); // OK

}

注意结束 ] [ 注意:如果仅用在非推导上下文中,模板形参不参予模板实参的推导。例如,

template <int i, typename T>

T deduce(typename A<T>::X x,  // T is not deduced here

T t, // but T is deduced here

typename B<i>::Y y);   // i is not deduced here

A<int> a;

B<77> b;

int x = deduce<77>(a.xm, 62, y.ym);

// T is deduced to be int, a.xm must be convertible to A<int>::X

// i is explicitly specified to be 77, y.ym must be convertible to B<77>::Y

注意结束 ]

15. 如果,在一个带有一个非类型模板参数的函数模板的声明中,该非类型模板形参被用于该函数形参列表中的一个表达式里,并且如果对应的模板实参被推导,该模板实参的类型必须与该模板形参的类型严格匹配(虽然对应一个类型为 bool 的模板形参的模板实参可以从一个数组边界推导,其结果将总是 true ,因为数组边界不会为 0 )。 [ 例如:除了一个从数组边界推导的模板实参可能具有任意整数类型。

template <int i> class A { /* ... */ };

template <short s> void f(A<s>);

void k1() {

A<1> a;

f(a); // error: deduction fails for conversion from int to short

f<1>(a); // OK

}

template <const short cs> class B { };

template <short s> void g(B<s>);

void k2() {

B<1> b;

g(b); // OK: cv-qualifiers are ignored on template parameter types

}

—例子结束 ]

16. 一个模板实参可以从一个函数指针或成员函数指针形式的实参推导,只要该重载函数组不包含函数模板,并且对重载函数组的匹配中不出现二义性。 [ 例如:

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

template <class T> void foo(T,int);

void g(int,int);

void g(char,int);

void h(int,int,int);

void h(char,int);

int m() {

f(&g); // error: ambiguous

f(&h); // OK: void h(char,int) is a unique match

f(&foo); // error: type deduction fails because foo is a template

}

—例子结束 ]

17. 一个模板类型形参不能从一个函数缺省实参的类型中推导。 [ 例如:

template <class T> void f(T = 5, T = 7);

void g() {

f(1); // OK: call f<int>(1,7)

f(); // error: cannot deduce T

f<int>(); // OK: call f<int>(5,7)

}

—例子结束 ]

18. 对应一个模板模板形参的模板实参,从在一个函数调用的实参列表中的,一个类模板特化的模板实参类型来推导。 [ 例如:

template <template <class T> class X> struct A { };

template <template <class T> class X> void f(A<X>) { }

template <class T> struct B { };

A<B> ab;

f(ab); // calls f(A<B>)

—例子结束 ] [ 注意:一个缺省的模板实参不能在一个函数模板声明或定义中指定;因而缺省模板实参不能用于改变模板实参推导 ]

函数 unify 执行真正的推导,如果成功返回 0 。其中,参数 tparms 是所处理模板的模板形参(这里它是只读的); targs 是保存了推导的实参的 vector (看到它是上面新产生的空 vector tempargs 。因为 unify 的每次调用只推导一对形参 / 实参,而 try_one_overload 本身也只通过 unify 推导函数指针对,这样必然涉及 unify 的递归调用;因此这个函数需要把当前推导结果合并入 targs ); arg 是要被推导的实参;而 parm 是对应的形参。

参数 strict 是下面标记的按位与的组合:

Ÿ           UNIFY_ALLOW_NONE :要求 PARM ARG 是严格匹配。

Ÿ           UNIFY_ALLOW_MORE_CV_QUAL :允许推导的 ARG ARG 具有更多的 cv- 限定(通过限定转换)。

Ÿ           UNIFY_ALLOW_LESS_CV_QUAL :允许推导的 ARG ARG 具有更弱的 cv- 限定。

Ÿ           UNIFY_ALLOW_DERIVED :允许推导的 ARG ARG 的一个模板基类类型,或者是指向由 ARG 指向的类的一个模板基类类型的指针。

Ÿ           UNIFY_ALLOW_INTEGER :允许推导出任一整数类型。更多信息,参见 TEMPLATE_PARM_INDEX 的说明。

Ÿ           UNIFY_ALLOW_OUTER_LEVEL :这是最外层的一个推导。用于确定限定转换的有效性。一个有效的限定转换必须具有 const 限定的,能达成要求额外 cv 限定的内层类型的指针,除了在最外层,那里 const 不被要求 [conv.qual] 。这个标记通常与标记 UNIFY_ALLOW_MORE_CV_QUAL 一同设置。

Ÿ           UNIFY_ALLOW_OUTER_MORE_CV_QUAL :这是最外层的一个推导,并且在这一点上 PARM 可以有更多 cv- 限定。

Ÿ           UNIFY_ALLOW_OUTER_LESS_CV_QUAL :这是最外层的一个推导,并且在这一点上 PARM 可以是更弱的 cv- 限定。

Ÿ           UNIFY_ALLOW_MAX_CORRECTION :这是一个 INTEGER_TYPE 的最大值。用在如果取值范围从一个大小规格,比如一个数组大小,导出。如果该大小由一个非类型模板形参 N 给定,其最大值将具有形式 N-1 。这个标记说明我们可以(实际上是必须)使 N 与( ARG + 1 )统一,是折叠 PARM 的通常规则的一个例外。

 

9735 static int

9736 unify (tree tparms, tree targs, tree parm, tree arg, int strict)                                 in pt.c

9737 {

9738    int idx;

9739    tree targ;

9740    tree tparm;

9741    int strict_in = strict;

9742

9743    /* I don't think this will do the right thing with respect to types.

9744      But the only case I've seen it in so far has been array bounds, where

9745      signedness is the only information lost, and I think that will be

9746      okay.  */

9747    while (TREE_CODE (parm) == NOP_EXPR)

9748      parm = TREE_OPERAND (parm, 0);

9749

9750    if (arg == error_mark_node)

9751      return 1;

9752    if (arg == unknown_type_node)

9753      /* We can't deduce anything from this, but we might get all the

9754        template args from other function args.  */

9755      return 0;

9756

9757    /* If PARM uses template parameters, then we can't bail out here,

9758      even if ARG == PARM, since we won't record unifications for the

9759      template parameters. We might need them if we're trying to

9760      figure out which of two things is more specialized.  */

9761    if (arg == parm && !uses_template_parms (parm))

9762      return 0;

9763

9764    /* Immediately reject some pairs that won't unify because of

9765      cv-qualification mismatches.  */

9766    if (TREE_CODE (arg) == TREE_CODE (parm)

9767        && TYPE_P (arg)

9768        /* It is the elements of the array which hold the cv quals of an array

9769          type, and the elements might be template type parms. We'll check

9770          when we recurse.  */

9771        && TREE_CODE (arg) != ARRAY_TYPE

9772        /* We check the cv-qualifiers when unifying with template type

9773          parameters below. We want to allow ARG `const T' to unify with

9774          PARM `T' for example, when computing which of two templates

9775          is more specialized, for example.  */

9776        && TREE_CODE (arg) != TEMPLATE_TYPE_PARM

9777        && !check_cv_quals_for_unify (strict_in, arg, parm))

9778      return 1;

 

9764 9778 行,如注释所言,因为不匹配的 cv- 限定拒绝某些对。看到这里我们需要保守些,仅考虑具有相同编码的模板形参及实参。这里滤除了 ARRAY_TYPE TEMPLATE_TYPE_PARM (代表形如“ T ”的模板类型形参),因为它们在后面将得到特殊的处理。

 

9659 static int

9660 check_cv_quals_for_unify (int strict, tree arg, tree parm)                                           in pt.c

9661 {

9662    int arg_quals = cp_type_quals (arg);

9663    int parm_quals = cp_type_quals (parm);

9664

9665    if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM

9666        && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))

9667    {

9668      /* Although a CVR qualifier is ignored when being applied to a

9669        substituted template parameter ([8.3.2]/1 for example), that

9670        does not apply during deduction [14.8.2.4]/1, (even though

9671        that is not explicitly mentioned, [14.8.2.4]/9 indicates

9672        this). Except when we're allowing additional CV qualifiers

9673        at the outer level [14.8.2.1]/3,1st bullet.  */

9674      if ((TREE_CODE (arg) == REFERENCE_TYPE

9675           || TREE_CODE (arg) == FUNCTION_TYPE

9676           || TREE_CODE (arg) == METHOD_TYPE)

9677         && (parm_quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)))

9678        return 0;

9679

9680      if ((!POINTER_TYPE_P (arg) && TREE_CODE (arg) != TEMPLATE_TYPE_PARM)

9681         && (parm_quals & TYPE_QUAL_RESTRICT))

9682        return 0;

9683    }

9684

9685    if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))

9686        && (arg_quals & parm_quals) != parm_quals)

9687      return 0;

9688

9689    if (!(strict & (UNIFY_ALLOW_LESS_CV_QUAL | UNIFY_ALLOW_OUTER_LESS_CV_QUAL))

9690        && (parm_quals & arg_quals) != arg_quals)

9691      return 0;

9692

9693    return 1;

9694 }

 

片段“ typename T::t ”指向定义在‘ T ’(‘ T ’是模板类型形参)上下文中的类型‘ t ’,在前端中为它构建了 TYPE_NAME_TYPE 节点。而“ T::template C ”指定义在‘ T ’(‘ T ’是模板类型形参)上下文中的模板‘ C ’, 在前端中构建了 UNBOUND_CLASS_TEMPLATE 节点。而 SCOPE_REF 是为特定重载的类方法的引用所构建的节点(例如,形如:“ void (T::*)() ”的模板类型形参)。

那么根据【 3 】, 14.8.2 .4 [temp.deduct.type] ,规则 4 :用在 nested-name-specifier 中(在上面表达式中的‘ T:: ’)的模板参数不能被推导。

 

unify (continue)

 

9780    if (!(strict & UNIFY_ALLOW_OUTER_LEVEL)

9781        && TYPE_P (parm) && !CP_TYPE_CONST_P (parm))

9782      strict &= ~UNIFY_ALLOW_MORE_CV_QUAL;

9783    strict &= ~UNIFY_ALLOW_OUTER_LEVEL;

9784    strict &= ~UNIFY_ALLOW_DERIVED;

9785    strict &= ~UNIFY_ALLOW_OUTER_MORE_CV_QUAL;

9786    strict &= ~UNIFY_ALLOW_OUTER_LESS_CV_QUAL;

9787    strict &= ~UNIFY_ALLOW_MAX_CORRECTION;

9788   

9789    switch (TREE_CODE (parm))

9790    {

9791      case TYPENAME_TYPE:

9792      case SCOPE_REF:

9793      case UNBOUND_CLASS_TEMPLATE:

9794        /* In a type which contains a nested-name-specifier, template

9795          argument values cannot be deduced for template parameters used

9796          within the nested-name-specifier.  */

9797        return 0;

9798

9799      case TEMPLATE_TYPE_PARM:

9800      case TEMPLATE_TEMPLATE_PARM:

9801      case BOUND_TEMPLATE_TEMPLATE_PARM:

9802        tparm = TREE_VALUE (TREE_VEC_ELT (tparms, 0));

9803

9804        if (TEMPLATE_TYPE_LEVEL (parm)

9805               != template_decl_level (tparm))

9806          /* The PARM is not one we're trying to unify. Just check

9807              to see if it matches ARG. */

9808          return (TREE_CODE (arg) == TREE_CODE (parm)

9809                 && same_type_p (parm, arg)) ? 0 : 1;

9810        idx = TEMPLATE_TYPE_IDX (parm);

9811        targ = TREE_VEC_ELT (targs, idx);

9812        tparm = TREE_VALUE (TREE_VEC_ELT (tparms, idx));

9813

9814        /* Check for mixed types and values.  */

9815        if ((TREE_CODE (parm) == TEMPLATE_TYPE_PARM

9816             && TREE_CODE (tparm) != TYPE_DECL)

9817           || (TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM

9818             && TREE_CODE (tparm) != TEMPLATE_DECL))

9819          return 1;

9820

9821        if (TREE_CODE (parm) == BOUND_TEMPLATE_TEMPLATE_PARM)

9822        {

9823           /* ARG must be constructed from a template class or a template

9824            template parameter.  */

9825          if (TREE_CODE (arg) != BOUND_TEMPLATE_TEMPLATE_PARM

9826             && (TREE_CODE (arg) != RECORD_TYPE || !CLASSTYPE_TEMPLATE_INFO (arg)))

9827            return 1;

9828

9829          {

9830            tree parmtmpl = TYPE_TI_TEMPLATE (parm);

9831            tree parmvec = TYPE_TI_ARGS (parm);

9832            tree argvec = TYPE_TI_ARGS (arg);

9833            tree argtmplvec

9834               = DECL_INNERMOST_TEMPLATE_PARMS (TYPE_TI_TEMPLATE (arg));

9835            int i;

9836

9837            /* The parameter and argument roles have to be switched here

9838              in order to handle default arguments properly. For example,

9839              template<template <class> class TT> void f(TT<int>)

9840              should be able to accept vector<int> which comes from

9841              template <class T, class Allocator = allocator>

9842               class vector.  */

9843

9844            if (coerce_template_parms (argtmplvec, parmvec, parmtmpl, 0, 1)

9845                 == error_mark_node)

9846              return 1;

9847     

9848            /* Deduce arguments T, i from TT<T> or TT<i>. 

9849              We check each element of PARMVEC and ARGVEC individually

9850              rather than the whole TREE_VEC since they can have

9851              different number of elements.  */

9852

9853            for (i = 0; i < TREE_VEC_LENGTH (parmvec); ++i)

9854            {

9855              tree t = TREE_VEC_ELT (parmvec, i);

9856

9857              if (unify (tparms, targs, t,

9858                      TREE_VEC_ELT (argvec, i),

9859                       UNIFY_ALLOW_NONE))

9860                return 1;

9861            }

9862          }

9863          arg = TYPE_TI_TEMPLATE (arg);

9864

9865          /* Fall through to deduce template name.  */

9866        }

9867

9868        if (TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM

9869           || TREE_CODE (parm) == BOUND_TEMPLATE_TEMPLATE_PARM)

9870        {

9871          /* Deduce template name TT from TT, TT<>, TT<T> and TT<i>.  */

9872

9873          /* Simple cases: Value already set, does match or doesn't.  */

9874           if (targ != NULL_TREE && template_args_equal (targ, arg))

9875            return 0;

9876          else if (targ)

9877            return 1;

9878        }

9879        else

9880        {

9881          /* If PARM is `const T' and ARG is only `int', we don't have

9882            a match unless we are allowing additional qualification.

9883            If ARG is `const int' and PARM is just `T' that's OK;

9884            that binds `const int' to `T'.  */

9885          if (!check_cv_quals_for_unify (strict_in | UNIFY_ALLOW_LESS_CV_QUAL,

9886                                    arg, parm))

9887            return 1;

9888

9889          /* Consider the case where ARG is `const volatile int' and

9890            PARM is `const T'. Then, T should be `volatile int'.  */

9891          arg = cp_build_qualified_type_real

9892                 (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);

9893          if (arg == error_mark_node)

9894            return 1;

9895

9896          /* Simple cases: Value already set, does match or doesn't.  */

9897          if (targ != NULL_TREE && same_type_p (targ, arg))

9898            return 0;

9899          else if (targ)

9900            return 1;

9901

9902           /* Make sure that ARG is not a variable-sized array. (Note

9903            that were talking about variable-sized arrays (like

9904            `int[n]'), rather than arrays of unknown size (like

9905            `int[]').) We'll get very confused by such a type since

9906            the bound of the array will not be computable in an

9907            instantiation. Besides, such types are not allowed in

9908            ISO C++, so we can do as we please here.  */

9909          if (variably_modified_type_p (arg))

9910            return 1;

9911        }

9912

9913        TREE_VEC_ELT (targs, idx) = arg;

9914        return 0;

 

我们已经看到,模板参数“ class T ”或“ typename T ”,由节点 TEMPLATE_TYPE_PARM 来表示;而形如:“ template <class T> class C ”的模板模板参数,则被构建为节点 TEMPLATE_TEMPLATE_PARM ;当对该模板模板参数给定一个实参(以 template-id 的形式),就产生 BOUND_TEMPLATE_TEMPLATE_PARM 节点(模板模板实参)。

记得 tparms 是模板形参的 vector (来自最上层的 fn_type_unification ,通过提取 fn DECL_INNERMOST_TEMPLATE_PARMS 得到,并且看到,即便递归入 unify tparms 也不改变)。

考虑 9804 行的比较,思考下面的语句:

template <template <class [U]opt > class TT, class T> void func(T, TT <T>) {}

template <class T> class B {};

main () {

    func (int, B <int> ());      // func (float, B <int> ()) fails for unify

}

注意到“ <> ”引入了一个新的绑定域,因此第一个语句是仅有的,能使用模板模板实参作为函数实参的形式(类似的形式,作为使用模板模板实参类模板成员);在这个 func 的声明中, U 是可选的,因为它在封闭它的‘ <> ’之外是不可见的。在为 TT 构建的 TEMPLATE_TEMPLATE_PARM 中,其内层参数与外层参数相同。因此当为 TT 推导这个内层参数‘ T ’的实参时(注意, func 不是 推导上下文), tparm 将具有层次 1 (因为 tparms 总是指向最外层的模板形参列表),并且当 parm 指向‘ TT ’中的‘ T ’时,它将具有层次 2 。在那一点上, parm 应该已经被成功推导,因为在 func 中‘ T ‘出现在‘ TT ’之前(反之亦然,如果在 func 中交换 T TT 的次序)。毫无疑问,对于这个情形, parm arg (即,之前推导的 parm )必然是相同的类型。

进一步的,如果用其他模板声明来替换上面的 U ,例如:

template <template < template <class [U]opt > class [V]opt > class TTT,

template <class [W]opt > class TT, class T> void func (TTT<TT>, T) {}

template <template <class T> class A> class Obj {};

template <class T> class B {};

main () {

    func(1, Obj<B> ()); // can change B to antoher template of the same form

}

并且考虑另一个例子:

template < template <class > class TTT, template <class > class TT,

          class T > void func(T, TT<T>, TTT<TT<T> >) {}

template <class T> class B {};

main () {

    func(1, B<int>(), B<B<int> >());   // the three parameters must match each other

}

上面的代码能够正确处理这两个例子,这归功于‘ <> ’引入新的封闭的绑定域。这也是为什么把 tparms 用作最外层模板的模板参数(以只读的方式)。

而如果 parm BOUND_TEMPLATE_TEMPLATE_PARM TYPE_TI_TEMPLATE (parm ) 则是由 parm 具现或特化的 TEMPLATE_DECL ,这个 TEMPLATE_DECL 将是直接父亲,但不是最泛化形式的模板。例如,在:

template <class T> struct S { template <class U> class F {}; };

S<int>::F<double> ”的 RECORD_TYPE 将具有,作为其 TYPE_TI_TEMPLATE ,“ template <class U> S<int>::F<U> ”。

那么 TYPE_TI_ARGS (parm ) 就是,从保存在 TYPE_TI_TEMPLATE (parm ) 中的 TEMPLATE_DECL 的最泛化形式,得到这个声明(即 parm )的模板实参。对于上例的 TYPE_TI_TEMPLATE ,对应的 TYPE_TI_ARGS 将是 {int, double} 。它们总是从这里指定的最泛化形式的模板来具现这个声明,所需要的一组完整的实参。

注意在 9825 行的条件,我们把它改写为如下:

  (TREE_CODE (arg) != BOUND_TEMPLATE_TEMPLATE_PARM ||

  ! (TREE_CODE (arg) == RECORD_TYPE && CLASSTYPE_TEMPLATE_INFO (arg)))

因此 arg 要么是 BOUND_TEMPLATE_TEMPLATE_PARM ,要么是类模板的具现。类似的, TYPE_TI_ARGS (arg ) 也保存了从 TYPE_TI_TEMPLATE (arg ) 的最泛化形式,得到 arg 的模板实参。

那么在 9844 行的对 coerce_template_parms 调用中,把所有模板实参转换到合适的类型,并返回一个包含最内层模板实参结果的 vector 。对这个函数中,向参数 parms 传递了 argtmplvec - TYPE_TI_TEMPLATE (arg ) 的模板形参,向参数 args 传递了 parmvec 来自 TYPE_TI_ARGS (parm ) 的目标实参。 9837 行的注释解释这样的原因在于接受缺省实参。

如果 argtmplvec 可以转换到 parmvec ,那么就可以检查是否可以从 argvec 推导出 parmvec (注意, argtmplvec 是具现这个模板的一组完整的模板实参,而 argvec 则是需要验证形参推导的实参集)。

如果对于模板模板实参 targ 不是 NULL ,这意味着我们已经推导了这个模板模板形参,它必须与这里给定的 arg 具有相同的模板声明。接着,从 9879 9914 行的代码,显示了如何推导模板形参‘ T ’。对于形参推导,这是我们最后到达的地方。

 

unify (continue)

 

9916      case TEMPLATE_PARM_INDEX:

9917        tparm = TREE_VALUE (TREE_VEC_ELT (tparms, 0));

9918

9919        if (TEMPLATE_PARM_LEVEL (parm)

9920            != template_decl_level (tparm))

9921           /* The PARM is not one we're trying to unify. Just check

9922            to see if it matches ARG.  */

9923          return !(TREE_CODE (arg) == TREE_CODE (parm)

9924                 && cp_tree_equal (parm, arg));

9925

9926        idx = TEMPLATE_PARM_IDX (parm);

9927         targ = TREE_VEC_ELT (targs, idx);

9928

9929        if (targ)

9930          return !cp_tree_equal (targ, arg);

9931

9932        /* [temp.deduct.type] If, in the declaration of a function template

9933          with a non-type template-parameter, the non-type

9934          template-parameter is used in an expression in the function

9935          parameter-list and, if the corresponding template-argument is

9936          deduced, the template-argument type shall match the type of the

9937          template-parameter exactly, except that a template-argument

9938          deduced from an array bound may be of any integral type.

9939          The non-type parameter might use already deduced type parameters.  */

9940        tparm = tsubst (TREE_TYPE (parm), targs, 0, NULL_TREE);

9941        if (!TREE_TYPE (arg))

9942          /* Template-parameter dependent expression. Just accept it for now.

9943            It will later be processed in convert_template_argument.  */

9944          ;

9945        else if (same_type_p (TREE_TYPE (arg), tparm))

9946          /* OK */ ;

9947        else if ((strict & UNIFY_ALLOW_INTEGER)

9948               && (TREE_CODE (tparm) == INTEGER_TYPE

9949                    || TREE_CODE (tparm) == BOOLEAN_TYPE))

9950           /* Convert the ARG to the type of PARM; the deduced non-type

9951            template argument must exactly match the types of the

9952            corresponding parameter.  */

9953          arg = fold (build_nop (TREE_TYPE (parm), arg));

9954        else if (uses_template_parms (tparm))

9955          /* We haven't deduced the type of this parameter yet. Try again

9956            later.  */

9957          return 0;

9958        else

9959          return 1;

9960

9961        TREE_VEC_ELT (targs, idx) = arg;

9962         return 0;

 

在上面的 9812 行,如果 tparm 指向 TEMPLATE_PARM_INDEX ,我们在解释解析器行为的例子中已经看过(参考 第二个例子 一节)。域 TEMPLATE_PARM_IDX (简称为 IDX )给出了该形参的索引(从 0 开始),而域 TEMPLATE_PARM_LEVEL (简称为 LEVEL )给出该形参的层次(从 1 开始)。例如:

   template <class T> // Index 0, Level 1

   struct S {

      template <class U, // Index 0, Level 2

                class V> // Index 1, Level 2

      void f();

   }; 

该例子产生 3 TEMPLATE_PARM_INDEX 。在这些节点中,一个由作为它们后裔的 TEMPLATE_PARM_INDEX 节点组成的链表,保存在 TEMPLATE_PARM_DESCENDANTS 域。第一个后裔将具有相同的 IDX ,但其 LEVEL 将少 1 TREE_CHAIN 域用于串接这些后裔。在后裔中,域 TEMPLATE_PARM_DECL 是该形参的声明,它要么是一个 TYPE_DECL ,要么是一个 CONST_DECL 。域 TEMPLATE_PARM_ORIG_LEVEL (简称为 ORIG_LEVEL )是最远父亲的 LEVEL ,即,该形参在被声明时所具有的初始 LEVEL 。例如,如果我们具现 S<int> ,我们将有:

  struct S<int> {

     template <class U, // Index 0, Level 1, Orig Level 2

                class V> // Index 1, Level 1, Orig Level 2

     void f();

   };

LEVEL 是,我们现在所关心的形参的层次;其 ORIG_LEVEL 是,当我们关心模板具现的细节时的层次。

注意到 targs 将是保存推导实参的 vector ,如果相应的项不为空,就表示该实参之前已经被推导了(或显式指定)。显然,这个已推导的实参必须与这里要推导的实参相同(这意味着同一个形参的两次不同的推导)。看到在 unify 及其递归中, targs 都不变化。那么在 9940 行,把它替换入由 parm 指定的模板,由 tparm 指向由此推导出的实参。对于成功有效的推导,毫无疑问, tparm 必须与 arg 的类型相同(除了使用 UNIFY_ALLOW_INTEGER 的情形)。

注意 9941 行,如果 TREE_TYPE NULL arg 应该是一个形如‘ T ’的 IDENTIFIER_NODE 。而如果在 9940 行所推导的 tparm 仍然依赖于模板形参,这些未推导的形参,在回到 try_one_overload 后将可能再被解析,或被发现是错误。现在可以安全地接受它。

相关文章推荐

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

 unify (continue) 9964     case PTRMEM_CST:9965      {9966       /* A pointer-to-member constant ...

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

5.13.1.1.2.1.2.          添加模板方法下面的隐式转换的机制将尝试查是否能隐式地调用某个模板转换操作符。这个过程不能被命令行选项“–fimiplicit-template”所关...

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

好了,现在让我们看一下GCC对我们例1的输出。例1:Vtable for CC::_ZTV1C: 12u entries0     0u        // vbase offset4     ...

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

当完成B1的处理时,build_vcall_and_vbase_vtbl_entries返回到处理类C的最顶层的调用中。这一次,build_vbase_offset_vtbl_entries不做任何事...

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

 继续前进之前,仔细阅读下面的注释。事实上,存在一个可能,我们会在下面失败退出。考虑以下例子:void f (unsigned short i) {}void f(unsigned char i)...

GCC-3.4.6源代码学习笔记(141-续2)

然后调用check_initializer来验证初始值。在这里参数flags是0。 4463   static tree4464   check_initializer (tree decl, tr...

GCC-3.4.6源代码学习笔记(176)

5.13.5.3.2.2.1.          按调用次序排序为了下面处理的方便起见,在1253行,把所有的cgraph_node按对应函数调用的次序排序,把有向图转化为队列(其满足拓扑排序(to...

GCC-3.4.6源代码学习笔记(158)

5.13.4.3.              迭代 – 发布全局聚集类的构造函数/ 析构函数接着在这个DO WHILE循环中,在2651行static_aggregates是一个链表,它包含了...

GCC-3.4.6源代码学习笔记(156)

5.13.3.      为基本类型产生tinfo操持完PCH文件,回到finish_file中继续为汇编代码的生成而奋斗。接下来编译器将为基本类型准备tinfo。 finish_file (cont...

GCC-3.4.6源代码学习笔记(170)

5.13.5.2.2.1.1.3.    发布汇编代码回到output_constant_def_contents,如果变量有大于1字节的对齐要求,还要调用ASM_OUTPUT_ALIGN来输出这个...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:GCC-3.4.6源代码学习笔记(147-续1)
举报原因:
原因补充:

(最多只允许输入30个字)