C++模板朋友奇怪的行为

Gre*_*reg 6 c++ templates friend

我在下面的代码中看到了一些我无法解释的内容.在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
}
Run Code Online (Sandbox Code Playgroud)

Ric*_*den 9

在您的例子中,参数的类型Sfoo2C1.朋友关系存在于T2<S>和之间T1<S>.虽然C1源于T1<C1>它并没有成为所有朋友的朋友T1<C1>.

在表达式中pT1->bar,bar找到C1并且,因为友谊没有传递给派生类,所以它是私有的.

您的例子是使用虚拟功能,因此这可以通过明确提到要解决bar我们班的一个朋友:

void foo2(S *pT1) { pT1->template T1<S>::bar(); }
Run Code Online (Sandbox Code Playgroud)

访问检查现在成功.

'03 C++标准中的参考文献是11.4/10,它只是说:

友谊既不是遗传也不是传递.

感谢Potatoswatter的评论.通过使用qualified-idfor id-expession,我们禁用虚拟调度(5.2.2/1):

如果所选函数是非虚拟的,或者类成员访问表达式中的id-expression是quali-ed-id,则调用该函数.

我们可以添加一个非虚拟调度程序,或者我们可以先将参数转换为基类型然后进行调用:

 void foo2(S *pT1) { static_cast< T1<S>* > (pT1)->bar(); }
Run Code Online (Sandbox Code Playgroud)

现在可以根据需要进行虚拟调度.

  • +1仅用于zany函数调用语法,但是这会破坏OP可能想要的"虚拟"功能.最好是实现一个非虚拟调度程序功能,可由朋友访问并用于调用私有虚拟"bar". (2认同)