Template cannot access its own protected members? huh?

C

cpisztest

I got an error message

"error C2248: 'RetainedV2<CardContextV2>::Retain' : cannot access protected member declared in class 'RetainedV2<CardContextV2>'

How exactly does that work? The class wants to access its own protected member. Why not?

I double and triple checked and CardContextV2 does not make any calls at all to RetainedV2.

However RetainedV2 does make calls to protected methods of CardContextV2, but CardContextV2 has declared those methods to be friend-ed.

I do not see why I must friend both ways.

I can't post the code and I know that's the first thing people will reply with. The company would frown on that. Instead try to explain to me how this error condition may occur at all. The way it reads, the message itself makes no sense.
 
V

Victor Bazarov

I got an error message

"error C2248: 'RetainedV2<CardContextV2>::Retain' : cannot access protected member declared in class 'RetainedV2<CardContextV2>'
[..]
I can't post the code and I know that's the first thing people will
reply with. The company would frown on that. Instead try to explain to
me how this error condition may occur at all. The way it reads, the
message itself makes no sense.

Position the cursor on the error number (C2248) in the output window,
then press F1. Read Microsoft's explanation about that error.

If you want us to analyze the code and offer work-arounds, we need to
see the code. Don't post it as it is. Distill it. Rename the classes,
the templates, the types, the members. Remove irrelevant parts. Make
sure that the distilled code still gives you the same error message when
compiled with the same compiler. Then post.

You feel frustrated by this, and you may think that copy-pasting the
error message and airing out your frustration is enough to get help. It
ain't, sorry.

V
 
C

cpisztest

On 4/10/2014 2:43 PM, @gmail.com wrote: SNIP
Position the cursor on the error number (C2248) in the output window,
then press F1. Read Microsoft's explanation about that error.

Already done.

"Members of a derived class cannot access private members of a base class."
Nothing is derived from the class in the error message. Nothing.
Remove irrelevant parts. Make
sure that the distilled code still gives you the same error message when
compiled with the same compiler. Then post.

If I knew which parts were relevant and which weren't, I'd already have the answer to my question, no?
 
C

cpisztest

On 4/10/2014 2:43 PM, (e-mail address removed) wrote:
If you want us to analyze the code and offer work-arounds, we need to
see the code. Don't post it as it is. Distill it. Rename the classes,
the templates, the types, the members. Remove irrelevant parts. Make
sure that the distilled code still gives you the same error message when
compiled with the same compiler. Then post.
SNIP

Here we go. You can replace boost::shared_ptr with std::shared_ptr in C++'11:


// Boost Includes
#include <boost/shared_ptr.hpp>

//------------------------------------------------
class IRetainable
{
public:
virtual ~IRetainable() {};
protected:
virtual void Relinquish() = 0;
virtual void Retain() = 0;
};

//------------------------------------------------
class MyTestClass;

//------------------------------------------------
template<class T>
class RetainedV2
{
public:
// TODO - This is my question:
/// I am not sure why we must friend possible T types of this class
// What are they calling? I know we are calling their Retain and Relinquish,
// but not the other way around.
/// If we do not, the compiler gives a nonsensical error message:
// 'RetainedV2<MyTestClass>::Retain' : cannot access protected member declared in class 'RetainedV2<MyTestClass>'
// The class cannot access its own protected members? huh?
//friend MyTestClass;

RetainedV2(const boost::shared_ptr<T> & resource);
RetainedV2(const RetainedV2<T> & rhs);
~RetainedV2();
RetainedV2<T> & operator = (const boost::shared_ptr<T> & rhs);
boost::shared_ptr<T> operator -> () const;
protected:
void Retain();
void Relinquish();
private:
bool m_isRetained;
boost::shared_ptr<T> m_resource;
};

//------------------------------------------------
template<class T>
RetainedV2<T>::RetainedV2(const boost::shared_ptr<T> & resource)
:
m_resource(resource),
m_isRetained(false)
{
Retain();
}

//------------------------------------------------
template<class T>
RetainedV2<T>::RetainedV2(const RetainedV2<T> & rhs)
:
m_resource(rhs.m_resource),
m_isRetained(false)
{
Retain();
}

//------------------------------------------------
template<class T>
RetainedV2<T>::~RetainedV2()
{
Relinquish();
}

//------------------------------------------------
template<class T>
RetainedV2<T> & RetainedV2<T>::eek:perator = (const boost::shared_ptr<T> & rhs)
{
Relinquish();
m_resource = rhs;
Retain();

return *this;
}

//------------------------------------------------
template<class T>
void RetainedV2<T>::Retain()
{
if (m_resource && !m_isRetained)
{
m_resource->Retain();
m_isRetained = true;
}
}

//------------------------------------------------
template<class T>
void RetainedV2<T>::Relinquish()
{
if (m_resource && m_isRetained)
{
m_resource->Relinquish();
m_isRetained = false;
}
}

//------------------------------------------------
template<class T>
boost::shared_ptr<T> RetainedV2<T>::eek:perator -> () const
{
return m_resource;
}

//------------------------------------------------
//------------------------------------------------
class MyTestClass : private IRetainable
{
public:
typedef boost::shared_ptr<MyTestClass> SharedPtr;

// I understand why these friend declarations are needed.
// Because these methods cal protected methods in this class.
// and I need them to be protected, because I don't want anyone else
// to be able to call them.
friend void RetainedV2<MyTestClass>::Retain();
friend void RetainedV2<MyTestClass>::Relinquish();

MyTestClass()
{
}
~MyTestClass()
{
}

protected:
void Retain()
{
}
void Relinquish()
{
}
};

//------------------------------------------------
int main()
{
boost::shared_ptr<MyTestClass> myTestClass(new MyTestClass());
RetainedV2<MyTestClass> retained(myTestClass);

return 0;
}
 
V

Victor Bazarov

//------------------------------------------------
template<class T>
class RetainedV2 [...]

Thanks. I shortened your code to this:

#include <boost/shared_ptr.hpp>

template<class T>
class R2
{
public:
R2(const boost::shared_ptr<T> & resource);
protected:
void Retain();
void Relinquish();
};

class Test
{
public:
friend void R2<Test>::Retain();
friend void R2<Test>::Relinquish();
};

//------------------------------------------------
int main()
{
boost::shared_ptr<Test> t(new Test());
R2<Test> retained(Test);

return 0;
}

It still gives the same error. Is it easier to figure out why? Do you
need a hint? The code actually can be shortened even further:

template<class T>
class R2
{
protected:
void Retain();
};

class Test
{
friend void R2<Test>::Retain();
};

int main()
{
R2<Test> r;
}

Same error! Do you still not see it?




























In class 'Test' the 'R2<Test>::Retain' is not accessible!!! It's
protected, and 'Test' is not derived from 'R2<Test>'.

Simple as that...


V
 
C

cpisztest

On 4/10/2014 5:06 PM, (e-mail address removed) wrote:
SNIP
...The code actually can be shortened even further:

template<class T>
class R2
{
protected:
void Retain();
};

class Test
{
friend void R2<Test>::Retain();
};

int main()
{
R2<Test> r;
}

Same error! Do you still not see it?

In class 'Test' the 'R2<Test>::Retain' is not accessible!!! It's
protected, and 'Test' is not derived from 'R2<Test>'.

Simple as that...

I guess I am misinterpreting the word "access." In my mind, looking at the code above, no one is accessing anything. I was under the impression that "access" in the error message above had to mean: "Somewhere in the implementation of the code that comes from expanding R2<Test>, someone is calling a private or protected method or looking at private or protected data." In your example, noone calls anything and noone looks at any data at all. It is as if simply declaring a friend _is access_. Right?

So, how do I allow the template class access to the protected and private methods and members of it's T arguments properly?
 
V

Victor Bazarov

[..]
I guess I am misinterpreting the word "access." In my mind, looking
at
the code above, no one is accessing anything. I was under the impression
that "access" in the error message above had to mean: "Somewhere in the
implementation of the code that comes from expanding R2<Test>, someone
is calling a private or protected method or looking at private or
protected data." In your example, noone calls anything and noone looks
at any data at all. It is as if simply declaring a friend _is access_.
Right?

Well, yes, according to the Standard, [class.friend]/9:

<<A name nominated by a friend declaration shall be accessible in the
scope of the class containing the friend
declaration.>>
So, how do I allow the template class access to the protected and
private methods and members of it's T arguments properly?

There are two ways. One, the template could grant the template argument
type friendship so that the type can access the protected members of the
template for whatever they are needed:

#include <iostream>

class Base
{
public:
virtual void foo() = 0;
};

template<class T> class NeedsAccess : public Base
{
friend T;
protected:
void foo() { T::doStuff(); } // protected overrider
};

class Arg
{
friend void NeedsAccess<Arg>::foo();
static void doStuff() { std::cout << "stuff\n"; } // private
};

int main()
{
NeedsAccess<Arg> na;
Base *pb = &na;
pb->foo();
}

If that solution doesn't look good, since 'Arg' doesn't need access to
everything except a couple of functions, you could make them both
befriend some intermediary, and let that intermediary conduct all the
necessary communication between the objects.

There are examples of that on the Web, I am sure.

V
 
C

cpisztest

I guess I am misinterpreting the word "access." In my mind, looking

the code above, no one is accessing anything. I was under the impression

that "access" in the error message above had to mean: "Somewhere in the

implementation of the code that comes from expanding R2<Test>, someone

is calling a private or protected method or looking at private or

protected data." In your example, noone calls anything and noone looks

at any data at all. It is as if simply declaring a friend _is access_.

Right?



Well, yes, according to the Standard, [class.friend]/9:



<<A name nominated by a friend declaration shall be accessible in the

scope of the class containing the friend

declaration.>>


So, how do I allow the template class access to the protected and
private methods and members of it's T arguments properly?



There are two ways. One, the template could grant the template argument

type friendship so that the type can access the protected members of the

template for whatever they are needed:



#include <iostream>



class Base

{

public:

virtual void foo() = 0;

};



template<class T> class NeedsAccess : public Base

{

friend T;

protected:

void foo() { T::doStuff(); } // protected overrider

};



class Arg

{

friend void NeedsAccess<Arg>::foo();

static void doStuff() { std::cout << "stuff\n"; } // private

};



int main()

{

NeedsAccess<Arg> na;

Base *pb = &na;

pb->foo();

}



If that solution doesn't look good, since 'Arg' doesn't need access to

everything except a couple of functions, you could make them both

befriend some intermediary, and let that intermediary conduct all the

necessary communication between the objects.



There are examples of that on the Web, I am sure.



V


Thanks Victor.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,787
Messages
2,569,630
Members
45,335
Latest member
Tommiesal

Latest Threads

Top