我在以下代码中看到了一些我无法解释的内容。在VS6,VS9和GCC T2 :: foo2()下给出错误:'bar':无法访问在类'C1'中声明的受保护成员。但是如果你删除C1 :: bar(),它会编译并正确运行,即使T2仍在访问受保护的C1B:bar(),你认为这也是同样的问题。
注意,在T2 :: foo2()中,您可以将'pT1'转换为'T1 *'并且一切正常,但这仍然无法解释为什么允许C1B :: bar(),但C1: :bar()不是。
template<class S> class T2;
template<class T> class T1
{
//template<class T> friend class T2; --> this doesn't compile under VS6
friend class T2<T>;
protected:
virtual void bar() { printf("T1\n"); }
};
template<class S> class T2
{
public:
void foo1(T1<S> *pT1) { pT1->bar(); } // --> ok, makes sense, this works either way
void foo2(S *pT1) { pT1->bar(); } // --> this fails to compile if C1::bar() is defined, but works for C1B::foo() ???
};
class C1 : public T1<C1>
{
protected:
virtual void bar() { printf("C1\n"); } // --> comment this out and foo2 will compile
};
class C1B : public C1
{
protected:
virtual void bar() { printf("C1B\n"); }
};
class C2 : public T2<C1>
{
};
void test(void)
{
C1B c1b;
C2 c2;
c2.foo1(&c1b);
c2.foo2(&c1b); // --> fails to compile if C1::bar() exists
}
答案 0 :(得分:9)
在您的示例中,S
中参数foo2
的类型为C1
。朋友关系存在于T2<S>
和T1<S>
之间。虽然C1
来自T1<C1>
,但它并未成为T1<C1>
所有朋友的朋友。
在表达式pT1->bar
中,bar
位于C1
中,由于友谊未传递给派生类,因此它是私有的。
您的示例正在使用虚拟函数,因此可以通过明确引用我们班级的朋友 的bar
来解决此问题:
void foo2(S *pT1) { pT1->template T1<S>::bar(); }
访问检查现在成功。
'03 C ++标准中的参考文献是11.4 / 10,它只是说:
友谊既不是遗传也不是传递。
感谢Potatoswatter的评论。通过qualified-id
使用id-expession
,我们禁用虚拟调度(5.2.2 / 1):
如果所选函数是非虚拟的,或者类成员访问表达式中的id-expression是quali-ed-id,则调用该函数。
我们可以添加非虚拟调度程序,或者我们可以先将参数转换为基类型然后进行调用:
void foo2(S *pT1) { static_cast< T1<S>* > (pT1)->bar(); }
现在可以根据需要进行虚拟调度。