9

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;
}
jlconlin
  • 14,206
  • 22
  • 72
  • 105
  • 1
    Not sure if this is what you're aiming for, but this compiles: http://codepad.org/2l2l1V44. Using protected inheritance and initializations instead of using Base::Base. – Victor Sand Jun 03 '14 at 11:54
  • That's a pretty weird error you ran into, my gut is telling me it should work and yet both gcc and clang seem to reject it for the reason you mentioned. – Matthieu M. Jun 03 '14 at 12:00
  • Also compiles on gcc 4.9 when adding `friend A* Create(int);` to class `A` and corresponding code in class `B`. – Felix Glas Jun 03 '14 at 12:13
  • @Snps: maybe, but why should it be necessary since the constructor is `public` in `A` ? – Matthieu M. Jun 03 '14 at 12:53
  • 1
    To put it simple : "a friend of my parent doesn't have to be my friend" – BЈовић Jun 03 '14 at 12:54
  • Ideally, the constructors should not be public in A or B. I just put them there to make the example compile – jlconlin Jun 03 '14 at 12:55
  • I [reduced the problem](http://ideone.com/jHKZQQ). Indeed `using Base::Base` is considered `protected` here. – Matthieu M. Jun 03 '14 at 12:59
  • 1
    @BЈовић I don't agree that this is a duplicate of the friendship inheritance question. [Here's an example](http://coliru.stacked-crooked.com/a/182d893df49bd5f1) with no friend functions that still fails to compile for the same reason. The issue here is with the inherited constructor retaining the same access in the derived class. – Praetorian Jun 03 '14 at 13:28
  • @Praetorian Very bad title and bad description of the problem, made me misunderstood the question. Fortunately, MatthieuM.'s example and your accepted answer clearly indicates that you two understood the problem. – BЈовић Jun 03 '14 at 13:56
  • @BЈовић: most interesting questions require reading between the lines because the OP is confused about what's going on. – Matthieu M. Jun 03 '14 at 14:09

3 Answers3

6

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

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Actually, it's pretty rare to need both `protected` AND `virtual`; you either have `protected` and non-virtual (no `delete` on `Base*`) or `public` and `virtual` (`delete` on `Base*`). The only case where both would be necessary is if you have some weird case of a `friend` (or child) calling `delete`, but this seem pretty weird. – Matthieu M. Jun 03 '14 at 14:07
  • 2
    And for the record, I don't know who had this idea of having `using Base::Base` behave differently than `using Base::foo`, but it certainly is confusing (once again). – Matthieu M. Jun 03 '14 at 14:10
  • @MatthieuM. You're right about the `protected` and `virtual` of course. I was thinking his `Create` function returns a `Base*`, and not `T*` when I wrote that (but then you wouldn't be able to delete anything created by the factory function). Agreed that the inheriting constructor behavior is unintuitive, haven't managed to come up with a good reason for why it was implemented this way. – Praetorian Jun 03 '14 at 14:17
  • Good job on getting the Standard quote by the way :) – Matthieu M. Jun 03 '14 at 14:24
3

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;
}
Community
  • 1
  • 1
Csq
  • 5,775
  • 6
  • 26
  • 39
  • The question is, why is `A::A(int)` considered `protected` when the `using Base::Base` is in the `public` section ? Generally, the `using` declaration [brings in the name](http://ideone.com/T6snVz). – Matthieu M. Jun 03 '14 at 13:01
  • @MatthieuM. I was wondering if that was the question myself but if the OP wanted to limit the construction of `A` and `B` then I don't understand how a public constructor with using would help in that. Also the OP wrote " the derived classes just use the base class constructors so their constructors are `protected` also." – Csq Jun 03 '14 at 13:06
  • Yes, I saw this remark, but then the OP commented "Ideally, the constructors should not be public in `A` or `B`. I just put them there to make the example compile.", which made me wonder what exactly was asked for. – Matthieu M. Jun 03 '14 at 14:12
1

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

Galik
  • 47,303
  • 4
  • 80
  • 117