Question

Normally nested structures have access to the owning classes public, protected and public member functions. There is also no problems calling a protected member function of a base class from within the nested structure, i.e. the following code compiles and works correctly:

#include <iostream>

class Base
{
public:
    Base()
    {}

protected:
    void baseProtectedFunc()
    {
        std::cout << __func__ << "Called for Base\n";
    }
};

class Derived : public Base
{
public:
    explicit Derived() : Base()
    {}

    void accessBaseProtectedFuncFromNested()
    {
        Nested myNested( this );
        myNested();
    }

private:
    struct Nested
    {
        explicit Nested( Derived* ptr ) : derived_( ptr ) 
        {}

        void operator()()
        {
            derived_->baseProtectedFunc();
        }

        Derived* derived_;
    };
};

int main( int, char** )
{
    Derived myDerived;
    myDerived.accessBaseProtectedFuncFromNested();
    return 0;
}

Now, consider the following code that uses mpl::inherit_linearly to generate the base classes for derived, using an mpl::vector of types:

#include <iostream>
#include <typeinfo>

#include <boost/mpl/vector.hpp>
#include <boost/mpl/inherit.hpp>
#include <boost/mpl/inherit_linearly.hpp>
#include <boost/mpl/for_each.hpp>

template<typename T>
class Base
{
public:
    Base()
    {}

protected:
    void baseProtectedFunc()
    {
        std::cout << __func__ << "Called for Base< " << typeid(T).name() << " >\n";
    }
};

typedef boost::mpl::vector< long
    , unsigned
    , bool
    , std::string
    > parameter_type_list_t;

typedef boost::mpl::inherit_linearly< parameter_type_list_t
                  , boost::mpl::inherit< boost::mpl::_1
                                       , Base< boost::mpl::_2 > > 
                  >::type base_types;

class Derived : public base_types
{
public:
    explicit Derived() : base_types()
    {}

    template<typename T>
    void accessBaseProtectedFuncFromNested()
    {
        Nested myNested( this );

        myNested.someFunc<T>();
    }

private:
    struct Nested
    {
        explicit Nested( Derived* ptr ) : derived_( ptr ) 
        {}

        template< typename T >
        void someFunc()
        {
            Base<T>* base = static_cast<Base<T>*>( derived_ );
            base->baseProtectedFunc();
        }

        Derived* derived_;
    };
};

int main( int, char** )
{
    Derived myDerived;
    myDerived.accessBaseProtectedFuncFromNested<unsigned>();

    return 0;
}

Using GCC version 4.4.6-3 (in c++03 and c++0x mode) the following error is generated:

friend-prot.cpp: In member function ‘void Derived::Nested::someFunc() [with T = unsigned int]’:
friend-prot.cpp:47:   instantiated from ‘void Derived::accessBaseProtectedFuncFromNested() [with T = unsigned int]’
friend-prot.cpp:82:   instantiated from here
friend-prot.cpp:17: error: ‘void Base<T>::baseProtectedFunc() [with T = unsigned int]’ is protected
friend-prot.cpp:72: error: within this context

If I make the function I am trying to call public the code compiles and works as expected.

I can get around the issue by adding an additional private member function to derived that simply forwards the call from Nested, i.e.:

struct Nested
{
    explicit Nested( Derived* ptr ) : derived_( ptr ) 
    {}

    template< typename T >
    void operator()()
    {
        derived_->forwarder<T>();
    }

    Derived* derived_;
}; 


template< typename T >
void forwarder()
{
    Base<T>::baseProtectedFunc();
}

I do not understand why I cannot call baseProtectedFunc() if it is protected when mpl::inherit is used.

Why am I allowed to call a base class protected function in the first example and not in the second?

Was it helpful?

Solution

You will find that you would get the same error if you wrote the forwarding function as

template <typename T>
void forwarder()
{
    Base<T>* base = static_cast<Base<T>*>( derived_ );
    base->baseProtectedFunc();
}

The problem is that the cast to a base pointer obscures to the compiler the fact that baseProtectedFunc is actually accessible from the current context with the specified pointer. As this is obscured, the compiler has to assume the access is not allowed through that pointer.

As the syntax you used in the forwarding function can also be used from within the nested class, the solution is rather simple and elegant:

struct Nested
{
    explicit Nested( Derived* ptr ) : derived_( ptr ) 
    {}

    template< typename T >
    void someFunc()
    {
        derived_->Base<T>::baseProtectedFunc(); /* <-- Changed */
    }

    Derived* derived_;
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top