我在下面的代码中看到一些我无法解释的东西。在 VS6、VS9 和 GCC T2::foo2() 下给出错误:“bar”:无法访问类“C1”中声明的 protected 成员。但是,如果您删除 C1::bar(),它会正确编译和运行,即使 T2 仍在访问 protected 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
}
最佳答案
在您的示例中,参数的类型 S
在 foo2
是C1
. T2<S>
之间存在好友关系和 T1<S>
.虽然C1
源自 T1<C1>
它不会成为所有 friend 的 friend T1<C1>
.
在表达式 pT1->bar
中, bar
在 C1
中找到而且,由于友元不会传递给派生类,因此它是私有(private)的。
您的示例使用虚函数,因此可以通过显式引用 bar
来解决这个问题那是我们类的一个 friend :
void foo2(S *pT1) { pT1->template T1<S>::bar(); }
现在访问检查成功。
'03 C++ 标准中对此的引用是在 11.4/10 中,它只是说:
Friendship is neither inherited nor transitive.
感谢 Potatoswatter 的评论。通过使用 qualified-id
对于 id-expession
,我们禁用虚拟调度(5.2.2/1):
If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called.
我们可以添加一个非虚拟调度器,或者我们可以先将参数转换为基本类型,然后再进行调用:
void foo2(S *pT1) { static_cast< T1<S>* > (pT1)->bar(); }
现在可以根据需要进行虚拟调度。
关于C++ 模板 friend 奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5968273/