5.9.2.2. 是否從友元通路
如果 protected_accessible_p 傳回非 0 值,目前類的派生樹包含了 decl ,而且 type 是目前類的一個基類( protected_accessible_p 的 837 行語句塊保證),那麼可以直接去檢查其通路屬性,而且 protected 以上即可。否則,如果目前類的派生樹不包含 decl ,或者 type 不是目前類的基類,那麼需要檢查目前作用域是否是可以通路 decl 的友元。函數 friend_accessible_p 的參數 scope (目前作用域)由下面的 current_scope 獲得。
550 tree
551 current_scope (void) in search.c
552 {
553 if (current_function_decl == NULL_TREE)
554 return current_class_type ;
555 if (current_class_type == NULL_TREE)
556 return current_function_decl ;
557 if ((DECL_FUNCTION_MEMBER_P (current_function_decl )
558 && same_type_p (DECL_CONTEXT (current_function_decl ),
559 current_class_type ))
560 || (DECL_FRIEND_CONTEXT (current_function_decl )
561 && same_type_p (DECL_FRIEND_CONTEXT (current_function_decl ),
562 current_class_type )))
563 return current_function_decl ;
564
565 return current_class_type ;
566 }
current_class_type 和 current_function_decl 的設定情況如下表給出。
current_class_type | current_function_decl | |
global | NULL | NULL |
function-local | NULL | SET |
class-local | SET | NULL |
class->function * | SET | SET |
function->class ** | SET | SET |
* :包括類的函數友元。 ** : GNU 的擴充,函數内的局部類
這時需要考慮的情況就是:接受目前域(可以是類,或者函數)作為友元的類包含了 decl ,而且 type 是其的一個基類。在函數 friend_accessible_p 中,參數 binfo 來自 type 。
855 static int
856 friend_accessible_p (tree scope, tree decl, tree binfo) in search.c
857 {
858 tree befriending_classes;
859 tree t;
860
861 if (!scope)
862 return 0;
863
864 if (TREE_CODE (scope) == FUNCTION_DECL
865 || DECL_FUNCTION_TEMPLATE_P (scope))
866 befriending_classes = DECL_BEFRIENDING_CLASSES (scope);
867 else if (TYPE_P (scope))
868 befriending_classes = CLASSTYPE_BEFRIENDING_CLASSES (scope);
869 else
870 return 0;
871
872 for (t = befriending_classes; t; t = TREE_CHAIN (t))
873 if (protected_accessible_p (decl, TREE_VALUE (t), binfo))
874 return 1;
875
876
878 if (TYPE_P (scope))
879 for (t = TYPE_CONTEXT (scope); t && TYPE_P (t); t = TYPE_CONTEXT (t))
880 if (protected_accessible_p (decl, t, binfo))
881 return 1;
882
883 if (TREE_CODE (scope) == FUNCTION_DECL
884 || DECL_FUNCTION_TEMPLATE_P (scope))
885 {
886
888 if (DECL_CLASS_SCOPE_P (decl)
889 && friend_accessible_p (DECL_CONTEXT (scope), decl, binfo))
890 return 1;
891
892
893 if (DECL_TEMPLATE_INFO (scope))
894 {
895 int ret;
896
898 ++processing_template_decl;
899 ret = friend_accessible_p (DECL_TI_TEMPLATE (scope), decl, binfo);
900 --processing_template_decl;
901 return ret;
902 }
903 }
904 else if (CLASSTYPE_TEMPLATE_INFO (scope))
905 {
906 int ret;
907
909 ++processing_template_decl;
910 ret = friend_accessible_p (CLASSTYPE_TI_TEMPLATE (scope), decl, binfo);
911 --processing_template_decl;
912 return ret;
913 }
914
915 return 0;
916 }
上面的 befriending_classes 就是目前函數或類作為友元的類的清單。如果對于這些類, protected_accessible_p 還是傳回 0 ,如果 scope 代表一個類,那麼這裡的做法是繼續逐級檢查包含 scope 的類;而如果 scope 代表一個類方法,則繼續考察其所在類作為友元的情形。 893 行表示該方法是一個模闆函數, DECL_TI_TEMPLATE 是代表其特化或具現的節點。而 904 行表示是一個模闆類, CLASSTYPE_TI_TEMPLATE 是代表其特化或具現的節點。這是考察模闆的特化類型是否是所期望的友元。
5.9.2.3. 确定通路屬性
從上面看到,如果 protected_accessible_p 或 friend_accessible_p 傳回非 0 值,表明 decl 和 type 都在同一棵派生樹裡。那麼接下來,在 accessible_p 的 1000 行,調用 access_in_type 來考察 type 對 decl 的通路屬性。如果結果在 protected 級别以上,毫無疑問, type 可以通路 decl 。但是對于友元來說, private 級别也不是問題。是以,如果結果是 private ,那麼還要看一下是否為友元。而且還有更複雜的情況,比如:
class B;
class A {
private :
int i;
friend void f(B*);
};
class B : public A { };
void f(B* p) {
p->i = 1; // OK: B* can be implicitly cast to A*, and f has access to i in A
}
這通過後序周遊 type 的派生樹時,執行 dfs_accessible_p 的方法來查找。 BINFO_ACCESS 在 1000 行的 access_in_type 執行時,就被設定好了。如果它是 ak_none ,就表明該類與 decl 無關。
778 static tree
779 dfs_accessible_p (tree binfo, void *data ATTRIBUTE_UNUSED) in search.c
780 {
781 access_kind access;
782
783 BINFO_MARKED (binfo) = 1;
784 access = BINFO_ACCESS (binfo);
785 if (access != ak_none
786 && is_friend (BINFO_TYPE (binfo), current_scope ()))
787 return binfo;
788
789 return NULL_TREE;
790 }
類似的, dfs_accessible_queue_p 傳回合适的 binfo 來遞歸地進入。它以中序周遊派生樹。
759 static tree
760 dfs_accessible_queue_p (tree derived, int ix, void *data ATTRIBUTE_UNUSED)
761 {
762 tree binfo = BINFO_BASETYPE (derived, ix);
763
764 if (BINFO_MARKED (binfo))
765 return NULL_TREE;
766
767
769 if (BINFO_BASEACCESS (derived, ix) != access_public_node
770 && !is_friend (BINFO_TYPE (derived), current_scope ()))
771 return NULL_TREE;
772
773 return binfo;
774 }
這一次,隻要找到一個合适的類,就傳回。