C++11 Declaring factory a friend of base class

江枫思渺然 提交于 2019-12-01 03:55:45

When you inherit a constructor from a base class it retains the access of the original constructor, regardless of where you place the using declaration in the derived class.

From §12.9/4 [class.inhctor]

A constructor so declared has the same access as the corresponding constructor in X. ...

You can fix the error if you explicitly add constructors to derived classes instead of inheriting them from Base.

A(int i) : Base(i) {}

and

B(int i) : Base(i) {}

Live demo

Another solution, of course, is to make Base's constructor public. You could also make its destructor protected, but it's not necessary since the class cannot be instantiated anyway due to the pure virtual member function.

class Base {
    public:
        template<class T>
        friend T* Create(int);
        virtual void say() = 0;

        Base(int i): i(i) { }   // This won't compile
        int i;
    protected:
        ~Base() {}
};

Live demo

Csq

Friendship does not go down the inheritance tree. Create is friend of Base, and therefore can not access the protected A::A(int) and B::B(int).

Possible solutions include:

  • Make new friendships (A, B and further child classes should be friends of Create)
  • Use public constructors as mentioned by @Snps
  • Use an external class that only Base (and therefore also its friend, Create) can create and the rest can only copy. Idea from here.

Code for last solution:

#include <iostream>

using namespace std;

// Forward declaration
template<class T> T* Create(int i);

class Base {

        class AccessKey {
            friend class Base;
            AccessKey() {};
        public:
            AccessKey(const AccessKey& o) {}

        };
        static AccessKey getAccessKey() { return AccessKey(); }

    public:
        template<class T>
        friend T* Create(int);
        virtual void say() = 0;

        Base(int i, AccessKey k): i(i) { }   // This can be public as it can be called without and AccessKey object

    protected:
        int i;
};

// Factory for Base class
template<class T>
T* Create(int i){
    return new T(i, Base::getAccessKey());
}

class A: public Base {
    public:
        using Base::Base;
        void say() { cout << "I am A and have a value of " << i << endl; }
};

class B: public Base{
    public:
        using Base::Base;
        void say() { cout << "I am B and have a value of " << i << endl; }
};

int main(){
    cout << "I'm creating A." << endl;
    A* a = Create<A>(1);
    a->say();

    cout << "I'm creating B." << endl;
    B* b = Create<B>(2);
    b->say();

    return 0;
}

I would be tempted to make the Create() function a static member of Base and then just make all the derived classes friends of Base:

Run This

#include <iostream>

using namespace std;

class Base {

public:
    virtual ~Base() {}

    // Factory for Base class

    template<class T>
    static T* Create(int i) {
        static_assert(std::is_base_of<Base, T>::value, "Needs to be derived from Base");
        return new T(i);
    }

    virtual void say() = 0;

protected:
    Base(int i): i(i) { }
    int i;
};

class A: public Base {

    friend Base; // Allow Base to construct

public:
    using Base::Base;

    void say() { cout << "I am A and have a value of " << i << endl; }
};

class B: public Base {

    friend Base; // Allow Base to construct

public:
    using Base::Base;

    void say() { cout << "I am B and have a value of " << i << endl; }
};

int main(){
    cout << "I'm creating A." << endl;
    A* a = Base::Create<A>(1);
    a->say();

    cout << "I'm creating B." << endl;
    B* b = Base::Create<B>(2);
    b->say();

    return 0;
}

EDIT: Added static_assert

EDIT: Added link to run code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!