Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Template friend odd behavior

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
}
like image 499
Greg Avatar asked May 11 '11 17:05

Greg


People also ask

What is a friend template in C++?

Template friends. A template friend declaration can name a member of a class template A, which can be either a member function or a member type (the type must use elaborated-type-specifier ). Such declaration is only well-formed if the last component in its nested-name-specifier (the name to the left of the last ::) is a simple-template-id...

What is << in friend Declaration operator in C++?

In friend declaration operator<< refers to a non-template function, while its definition says it's a template function; they don't match. You can define it inline with the friend declaration (as non-template function):

Why does the friend function declaration “F” Fail?

The friend function declaration "f" will cause an error when the enclosing template class is instantiated with arguments that declare a friend function that does not match an existing definition. The function declares only one function because it is not a template but the function type depends on one or more template parameters.

Are default template arguments allowed on template friend declarations?

Default template arguments are only allowed on template friend declarations if the declaration is a definition and no other declarations of this function template appear in this translation unit.


1 Answers

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.

like image 60
Richard Corden Avatar answered Sep 23 '22 01:09

Richard Corden