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;
}
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
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