Click here to Skip to main content
15,881,424 members
Please Sign up or sign in to vote.
5.00/5 (3 votes)
VS 2012 gives an 'error C2248: ... cannot access protected member declared in class...' in the following minimal code stripped from a complete project. VS 2008 has no problems with it, and executes as expected.

The line right before the issue calls the protected member in question, and no errors are given, as it should be. The template's construction is when the problem appears.

C++
class DwlWinObject {
   };

class DwlDelegate {
   public:
      int junk;
      template <class T, void (T::*Method)(DwlWinObject*)>
      static DwlDelegate create(T* object_ptr, DwlWinObject * arg) {
         DwlDelegate d;
         d.junk = 12;
         return d;
         }
   };
   
class ModalBaseWindow : public DwlWinObject {
   protected:
      int a;
      void accept(DwlWinObject*) {
         a=0;
         }
   };

class MyModalWindow : public ModalBaseWindow {
   public:
      MyModalWindow() {
         accept(this);  //2008: No problem accessing 'accept'
         //But VS2012 gives C2248 compile error on the next line.
         //C2248 is 'cannot access protected member'
         //VS2008 has no problems with it, and executes as expected.
         DwlDelegate d = DwlDelegate::create<ModalBaseWindow,
                     &ModalBaseWindow::accept>(this, this);
         int test = d.junk;
         d.junk = 15;
         }
   };

void main() {
   MyModalWindow win;
   }


Is this a bug in Visual Studio 2012, or did something in the standards change? As I don't really want to make the accept routine public, is my best option to create an accept function in the MyModalWindow class which calls the ModalBaseWindow::accept? In the full application that's a lot of additional code for all of the subclasses involved for something which once worked.

Thanks,
David
Posted

The rules did change but I don't think that's the problem. I think it's just that Microsoft started applying the existing ones.
It's a tricky case but I think the problem can be summarized as trying to use a protected member function pointer as a template parameter to a function that does not have access to that member.
The two ways to solve this are to give the create function the access it needs or to unprotect the access function which is definitely the last resort.

The easiest way I can see to give the create function access without wider impact on the code is to make the delegate a friend of ModalBaseWindow

C#
class ModalBaseWindow : public DwlWinObject {

   friend class DwlDelegate;

   protected:
      int a;
      void accept(DwlWinObject*) {
         a=0;
         }
   };


The problem with this is that template member friend rules are about the outer limits of the C++ spec and are even more likely to be broken in the MSVC compiler than what you already have. Anyway, give it a try and let me know.
 
Share this answer
 
Comments
David O'Neil 13-Jul-13 8:06am    
Seldom used this before, so didn't see how to comment. But deleted my other reply now that it is abundantly obvious!

Unfortunately, friending it doesn't work. I'm seriously thinking about the public route to save time. Thanks for the input!
Matthew Faithfull 13-Jul-13 8:14am    
No problem. Your choice as to how much time to invest in it.
I tend to find there is usually a way to persuade these things to work but not always. If you have the opportunity then trying under a completely different compiler like the latest GCC or CLang might be a useful exercise.
If it works there without warnings then there's a good chance you're right and MS is wrong. If not then the combined set of errors/ warnings across several compiler might give enough clues to crack it.

There is another option which is to pass the member function pointer as a runtime parameter rather than having it as part of the signature of create. This obviously open up the possibility of having to change more code but it might not be too bad.
David O'Neil 13-Jul-13 9:17am    
You mean something like this?

class DwlWinObject {
};

class ModalBaseWindow;

template <class t>
class DwlDelegate {
typedef void (T::*FuncPtr)(DwlWinObject*);
protected:
FuncPtr ptrC;
public:
int junk;
static DwlDelegate create(T* object_ptr, DwlWinObject * arg, FuncPtr ptr) {
DwlDelegate d;
d.junk = 12;
return d;
}

};

class ModalBaseWindow : public DwlWinObject {
protected:
int a;
void accept(DwlWinObject*) {
a=0;
}
};

class MyModalWindow : public ModalBaseWindow {
public:
MyModalWindow() {
accept(this); //2008: No problem accessing 'accept'
//But VS2012 gives C2248 compile error on the next line.
//C2248 is 'cannot access protected member'
//VS2008 has no problems with it, and executes as expected.
DwlDelegate<mymodalwindow> d = DwlDelegate<mymodalwindow>::create(this, this, &ModalBaseWindow::accept);
int test = d.junk;
d.junk = 15;
}
};

void main() {
MyModalWindow win;
}

If so, that's also a no-go. I was surprised it took the 'd = line. [edit - I mean that it didn't barf up a template error instead of the 'can't access' error!] Templates make my head hurt!

Thanks,
David
Matthew Faithfull 13-Jul-13 9:34am    
When you say a no-go, do you get the same error? What about in combination with the friend declaration?
I find it hard to believe it won't let you do that. The only further step I can think of is making the function pointer typedef in the delegate public and actually cast the address of ModalBaseWindow::accept before you pass it but that is getting truly horrible and I don't believe it should be necessary.
...
Spotted one thing, your d = does need to be DwlDelegate <ModalBaseWindow> or it definitely isn't going to work as MyModalWindow doesn't have an accept function.

Templates make everyone's heads hurt, even the compiler writers. :-)
David O'Neil 13-Jul-13 10:08am    
Thank you for the feedback. Yes, I get the same error regardless of the approach. Even the following gives the exact same thing, whether or not I cast in the d = line. I don't have another compiler installed, and even though it would be fun to play and see, I'm going to skip on that for now.

class DwlWinObject {
};

template <class T>
class DwlDelegate {
public:
typedef void (T::*FuncPtr)(DwlWinObject*);
protected:
FuncPtr ptrC;
public:
int junk;
static DwlDelegate create(T* object_ptr, DwlWinObject * arg, FuncPtr ptr) {
DwlDelegate d;
d.junk = 12;
return d;
}
};

class ModalBaseWindow : public DwlWinObject {
friend class DwlDelegate<ModalBaseWindow>;
protected:
int a;
void accept(DwlWinObject*) {
a=0;
}
};

class MyModalWindow : public ModalBaseWindow {
public:
MyModalWindow() {
accept(this); //2008: No problem accessing 'accept'
//But VS2012 gives C2248 compile error on the next line.
//C2248 is 'cannot access protected member'
//VS2008 has no problems with it, and executes as expected.
DwlDelegate<modalbasewindow> d = DwlDelegate<modalbasewindow>::create(this, this,
(DwlDelegate<modalbasewindow>::FuncPtr)(&ModalBaseWindow::accept));
int test = d.junk;
d.junk = 15;
}
};

void main() {
MyModalWindow win;
}

(And don't ask me why a copy/paste results in ModalBaseWindow loosing its capitalization when inside < >)
Unfortunately I have no VS2012 at hand but I have a g++ that has always been my friend when I wanted to check whether a piece of code follows the standard or not. Using my VS2010 and g++4.5 (cygwin) I have put together the following example program that may serve you with some useful info:

C++
class DwlWinObject {};

class Delegate
{
public:
    virtual void Call(DwlWinObject* param) = 0;

    template <typename T>
    static Delegate* Create(T* const obj, void (T::*method)(DwlWinObject*));
};

template <typename T>
class TDelegate : public Delegate
{
public:
    virtual void Call(DwlWinObject* param)
    {
        (m_Obj->*m_Method)(param);
    }
    TDelegate(T* obj, void (T::*method)(DwlWinObject*))
    {
        m_Obj = obj;
        m_Method = method;
    }
private:
    T* m_Obj;
    void (T::*m_Method)(DwlWinObject*);
};

template <typename T>
Delegate* Delegate::Create(T* const obj, void (T::*method)(DwlWinObject*))
{
    return new TDelegate<T>(obj, method);
}

class ModalBaseWindow : public DwlWinObject
{
protected:
    void accept(DwlWinObject* param)
    {
        printf("%s(%p)\n", __FUNCTION__, param);
    }
};

class MyModalWindow : public ModalBaseWindow
{
public:
    MyModalWindow()
    {
        //accept(this);  //2008: No problem accessing 'accept'

        // compiles
        void (MyModalWindow::*Method)(DwlWinObject*) = &MyModalWindow::accept;

        // x.cpp: In constructor ‘MyModalWindow::MyModalWindow()’:
        // x.cpp:39:7: error: ‘void ModalBaseWindow::accept(DwlWinObject*)’ is protected
        // x.cpp:54:70: error: within this context
        //void (ModalBaseWindow::*Method)(DwlWinObject*) = &ModalBaseWindow::accept;

        // compiles
        Delegate* d = new TDelegate<MyModalWindow>(this, &MyModalWindow::accept);

        // x.cpp: In constructor ‘MyModalWindow::MyModalWindow()’:
        // x.cpp:39:7: error: ‘void ModalBaseWindow::accept(DwlWinObject*)’ is protected
        // x.cpp:59:72: error: within this context
        //Delegate* d = new TDelegate<ModalBaseWindow>(this, &ModalBaseWindow::accept);

        // compiles
        //Delegate* d = Delegate::Create<MyModalWindow>(this, &MyModalWindow::accept);

        // compiles
        //Delegate* d = Delegate::Create<ModalBaseWindow>(this, &MyModalWindow::accept);

        // x.cpp: In constructor ‘MyModalWindow::MyModalWindow()’:
        // x.cpp:46:7: error: ‘void ModalBaseWindow::accept(DwlWinObject*)’ is protected
        // x.cpp:81:75: error: within this context
        //Delegate* d = Delegate::Create<ModalBaseWindow>(this, &ModalBaseWindow::accept);

        // firing an event using the delegate
        d->Call(this);
    }
};

int main() {
    MyModalWindow win;
    return 0;
}


To me the conclusion is that instead of &ModalBaseWindow::accept you should try using &MyModalWindow::accept when you specify the method pointer.
 
Share this answer
 
Comments
David O'Neil 13-Jul-13 13:48pm    
That is the solution! It was simply in the "d = ... assignment that I needed to make certain everything referred to MyModalWindow, which, if you review the previous code snips, weren't the cases. Doing that, the virtual method ISN'T NEEDED - it can be a regular class function!

For clarity, here's the working code for the initial method:

class DwlWinObject {
};

template <class T>
class DwlDelegate {
public:
typedef void (T::*FuncPtr)(DwlWinObject*);
protected:
FuncPtr ptrC;
public:
int junk;
static DwlDelegate create(T* object_ptr, DwlWinObject * arg, FuncPtr ptr) {
DwlDelegate d;
d.junk = 12;
return d;
}
};

class ModalBaseWindow : public DwlWinObject {
protected:
int a;
void accept(DwlWinObject*) {
int a=0;
}
};

class MyModalWindow : public ModalBaseWindow {
public:
MyModalWindow() {
DwlDelegate<MyModalWindow> d = DwlDelegate<MyModalWindow>::create(this, this,
&MyModalWindow::accept);
int test = d.junk;
d.junk = 15;
}
};

void main() {
MyModalWindow win;
}

Of course, that means more rework than making everything public, but that's OK! Thank you very much!
pasztorpisti 13-Jul-13 14:05pm    
I'm glad I could help. The need for the virtual function depends on how "general" delegate you want. With the delegate base class + virtual function the delegate can be called by anyone knowing about the base class without knowing anything used by the actual delegate implementation (that it calls a method inside the window class). And btw, my code is buggy because it doesn't handle the lifetime of the delegate object and the base class doesnt have a virtual destructor but this is another story... :-)
pasztorpisti 13-Jul-13 14:30pm    
Hey, just don't forget one line from my example program:
Delegate* d = Delegate::Create<ModalBaseWindow>(this, &MyModalWindow::accept);
Here I specified ModalBaseWindow when I declared the pointer and I used MyModalWindow only in the expression that specifies the actual pointer value.
The same is true for method pointer declarations but I forgot this out form the example program:
void (ModalBaseWindow::*Method)(DwlWinObject*) = &MyModalWindow::accept;
This means that you have to replace ModalBaseWindow to MyModalWindow only when you specify the method pointer value, the pointer declarations can keep using the base class.
David O'Neil 13-Jul-13 14:42pm    
Thanks for the reminder. It has been a while since I've delved this deep into this stuff. And as far as the buggy code - for an example I won't say a thing! And since you caught it, the chance of getting to production is slim!
pasztorpisti 13-Jul-13 15:14pm    
I guess noone delves too deep into the pitfalls of C++ till it isn't necessary. It is an overly complex language with too many rules to memorize. I didn't know either about this visibility corner case before trying out things in my example program. You just have to find the most acceptable solution when you face a problem like this. Unfortunately its not always as clean as in this case.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900