Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 Declaring factory a friend of base class

I'm trying to create a factory for derived classes. I only want the factory to be able to create instances of the derived classes so I've made the base constructor protected; the derived classes just use the base class constructors so their constructors are protected also.

I tried to declare the factory as a friend of the base class so that it could access the protected constructor. When I compile using this command

clang++ -std=c++11 -stdlib=libc++ Friends.cpp -o Friends

I get this error:

Friends.cpp:23:20: error: calling a protected constructor of class 'A'
        return new T(i);
               ^
Friends.cpp:42:16: note: in instantiation of function template specialization 'Create<A>' requested
      here
        A* a = Create<A>(1);
           ^
Friends.cpp:30:25: note: declared protected here
             using Base::Base;
                    ^

Along with a similar error for derived class B.

I get the feeling from reading other questions on stackoverflow.com, that this isn't possible in C++11, but I'm not sure why. Can someone explain why this won't work and perhaps an alternative?

Example code

#include <iostream>

using namespace std;

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

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

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

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

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;
}
like image 767
jlconlin Avatar asked Jun 03 '14 11:06

jlconlin


1 Answers

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

like image 81
Praetorian Avatar answered Oct 16 '22 11:10

Praetorian