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

发布于 2024-11-06 08:24:04 字数 1237 浏览 0 评论 0原文

我在下面的代码中看到一些我无法解释的东西。在 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
}

I'm seeing something I can't explain in the following code. Under VS6, VS9, and GCC T2::foo2() gives the error: 'bar' : cannot access protected member declared in class 'C1'. But if you remove C1::bar(), it compiles and runs correctly, even though T2 is still accessing the protected C1B:bar(), which you would think would be the same problem.

Note, that in T2::foo2() you could cast 'pT1' to be a 'T1*' and everything is fine, but that still does not explain why C1B::bar() is allowed, but C1::bar() is not.

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
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不奢求什么 2024-11-13 08:24:04

在您的示例中,foo2 中的参数S 的类型是C1T2T1 之间存在好友关系。尽管C1派生自T1,但它并没有成为T1的所有友元的友元。

在表达式 pT1->bar 中,barC1 中找到,并且由于友谊没有传递给派生类,因此它是私人的。

您的示例使用的是虚函数,因此可以通过显式引用 类的友元 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 表达式是限定 id,则调用该函数。

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

 void foo2(S *pT1) { static_cast< T1<S>* > (pT1)->bar(); }

现在根据需要进行虚拟调度。

In your example, the type of the parameter S in foo2 is C1. The friend relationship exists between T2<S> and T1<S>. Although C1 derives from T1<C1> it does not become a friend of all the friends of T1<C1>.

In the expression pT1->bar, bar is found in C1 and, as the friendship is not passed down to the derived class, it is private.

Your example is using virtual functions so this can be addressed by explicitly referring to the bar that is a friend of our class:

void foo2(S *pT1) { pT1->template T1<S>::bar(); }

The access check now succeeds.

The reference for this in the '03 C++ standard is in 11.4/10, where it simply says:

Friendship is neither inherited nor transitive.

Thanks to Potatoswatter for his comment. By using the qualified-id for the id-expession, we disable virtual dispatch (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.

We can add a non-virtual dispatcher, or we can first convert the parameter to the base type and then make the call:

 void foo2(S *pT1) { static_cast< T1<S>* > (pT1)->bar(); }

Virtual dispatch now takes place as required.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文