f



How do I solve the one-way protection?

Hi!

I want to use the compiler to force correct initialization by forcing 
certain functions to be implemented in the subclasses (pure virtual 
functions only reach the first subclass). In my example below we can 
assume that the base class must be initialized by collecting, say class 
names.

class Base
{
public:
    Base();

protected:
    template<class T>
    void init_()
    {
       std::string cn = static_cast<T*>(this)->getClassName_();
       ... store cn in a list ...
    }
};

class Derived1 : public Base
{
public:
    Derived1() { init_<Derived1>(); }

protected:
    std::string getClassName_() const { return "Number 1"; }
};

class Derived2 : public Derived1
{
public:
    Derived2() { init_<Derived2>(); }

protected:
    std::string getClassName_() const { return "Number 2"; }
};


This does not compile unless I make getClassName_() public. Why is 
protection one-way and how can I get around it?


Thanks,
Daniel

-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
DeMarcus
8/6/2009 10:05:54 PM
comp.lang.c++.moderated 10738 articles. 1 followers. allnor (8506) is leader. Post Follow

3 Replies
306 Views

Similar Articles

[PageSpeed] 51

>    template<class T>
>    void init_()
>    {
>       std::string cn = static_cast<T*>(this)->getClassName_();
>       ... store cn in a list ...
>    }
> [...]
> This does not compile unless I make getClassName_() public. Why is
> protection one-way and how can I get around it?

When you convert "this" to a generic "T*" using static_cast<>, you no
longer benefit from being able to access protected members of T, even if
  T is the class you're calling from. For the simple reason that T can
now be anything, including "int" or "std::string". The compiler will
assume that you know what you're doing and T does have an accessible
(read: public) getClassName_() function, but no more than that.

You really don't need templates at all for what you're doing, that is
you can solve your problem either with templates, but no virtual
functions (in which case your member function needs to be public for the
reasons stated above), or without templates and with virtual member
functions, where you define getClassName_() as pure virtual in Base, and
simply call:

std::string cn = getClassName_();

in init_(). This will call the correct function for your objects.


-- 
Razvan Cojocaru
KeyID: 0x04CA34DE

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Razvan
8/7/2009 12:37:54 PM
On Aug 7, 12:05 am, DeMarcus <use_my_alias_h...@hotmail.com> wrote:
> Hi!
>
> I want to use the compiler to force correct initialization by forcing
> certain functions to be implemented in the subclasses (pure virtual
> functions only reach the first subclass). In my example below we can
> assume that the base class must be initialized by collecting, say class
> names.
>
> class Base
> {
> public:
>     Base();
>
> protected:
>     template<class T>
>     void init_()
>     {
>        std::string cn = static_cast<T*>(this)->getClassName_();
>        ... store cn in a list ...
>     }
>
> };
>
> class Derived1 : public Base
> {
> public:
>     Derived1() { init_<Derived1>(); }
>
> protected:
>     std::string getClassName_() const { return "Number 1"; }
>
> };
>
> class Derived2 : public Derived1
> {
> public:
>     Derived2() { init_<Derived2>(); }
>
> protected:
>     std::string getClassName_() const { return "Number 2"; }
>
> };
>
> This does not compile unless I make getClassName_() public. Why is
> protection one-way and how can I get around it?
>
> Thanks,
> Daniel

I think you've got to declare Base a friend in each derived class.
At least that's what people sometimes do with Curiously Recurring
Template Pattern, and your case is somewhat similar to CRTP.

Note that unless getClassName_ should be called from more
derived classes, you should declare them private, not protected.


HTH,
   Andy.


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Andrew
8/7/2009 12:40:44 PM
On 7 Aug, 05:05, DeMarcus <use_my_alias_h...@hotmail.com> wrote:
> Hi!
>
> I want to use the compiler to force correct initialization by forcing
> certain functions to be implemented in the subclasses (pure virtual
> functions only reach the first subclass). In my example below we can
> assume that the base class must be initialized by collecting, say class
> names.
>
> class Base
> {
> public:
>     Base();
>
> protected:
>     template<class T>
>     void init_()
>     {
>        std::string cn = static_cast<T*>(this)->getClassName_();
>        ... store cn in a list ...
>     }
>
> };
>
> class Derived1 : public Base
> {
> public:
>     Derived1() { init_<Derived1>(); }
>
> protected:
>     std::string getClassName_() const { return "Number 1"; }
>
> };
>
> class Derived2 : public Derived1
> {
> public:
>     Derived2() { init_<Derived2>(); }
>
> protected:
>     std::string getClassName_() const { return "Number 2"; }
>
> };
>
> This does not compile unless I make getClassName_() public. Why is
> protection one-way and how can I get around it?

There are two issues here: Why is protection one way? and How to get
the class name in the base class constructor?

Why is protection one way?
(A) I would think that most people would consider it unnatural to
allow special access to base classes. After all a base class is not
supposed to depend on the declaration of its derived classes (Although
it can depend on the definitions using virtual methods).
(B) What would you expect from:  Derived2()
{ init_<CompletelyUnrelated>(); }
(C) In general it is not valid to call ANY derived class member
function from a base class constructor because the derived object is
not constructed at that point. (This is why virtual methods aren't
virtual in a constructor)

How to get that class name to the base class constructor:
Short answer: Provide a protected constructor in the base class that
takes it as parameter.
Note that you never NEED to call a derived class member in a base
class constructor because it can never return anything that couldn't
have been passed as a parameter.
Occasionally it can be useful to have a STATIC derived class function
to compute the constructor parameter if this is complex.
In this specific example there is no reason for getClassName_ to not
be static as it does not depend on object state:

cn = T::getClassName_();


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Nick
8/7/2009 12:41:39 PM
Reply: