8

I have a component class that defines a static template method of how a Component should be created in general:

class Component {
protected:
    uint32_t id;

    Component(uint32_t id) :
            id(id) {
    }

    template<typename T, uint32_t C>
    static T* createComponent() {
        // content here not relevant
        return new T(someParameter);
    }

};

Then there is an implementation, for example a Button. The constructor of this class should not be used directly, instead there is a static method that calls the Component::createComponent template function.

class Button: public Component {
protected:
    Button(uint32_t id) :
            Component(id) {
    }
public:
    static Button* create();
};

The implementation looks like this, passing the type to instantiate and a constant thats used in creation:

Button* Button::create() {
    return createComponent<Button, UI_COMPONENT_BUTTON>();
}

Now the problem is, that the compiler complains with "error: 'Button::Button(uint32_t)' is protected". For my understanding, this constructor call should be OK as Button extends Component, but this seems to be a problem here.

How can I solve this?

maxdev
  • 2,491
  • 1
  • 25
  • 50
  • 1
    The complexity of your title is cracking me up but this is a good question. – BWG Dec 17 '14 at 15:31
  • I couldn't imagine an easier one that describes the basic problem :D – maxdev Dec 17 '14 at 15:35
  • Well, your constructor doesn't do anything that the 'Create' method couldn't, so just don't use any Ctor? – BWG Dec 17 '14 at 15:35
  • The problem is that there is also a `Window` class that does not use the `createComponent` function, though I still wanted to force it to pass the id to the constructor. But yes, you are right, I can just add that logic to the template function. :) Thanks! – maxdev Dec 17 '14 at 15:39

3 Answers3

4

Since your create() function won't be able to deal with further inherited classes you can take advantage of this and not create a Button but, instead, a generic derived, protected, derived class which would have access to your protected constructor:

class Component {
    uint32_t id;
    template <typename T>
    struct Concrete: T {
        Concrete(uint32_t id): T(id) {}
    };
protected:
    Component(uint32_t id) :
        id(id) {
    }

    template<typename T, uint32_t C>
    static T* createComponent() {
        // content here not relevant
        return new Concrete<T>(C);
    }
};

class Button:
    public Component {
protected:
    Button(uint32_t id): Component(id) {}
public:
    static Button* create() {
         return createComponent<Button, UI_COMPONENT_BUTTON>();
    }
};
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 1
    Yes, `Base::createComponent()` when called with a `ConcreteButton`, indeed, das return a `Concrete – Dietmar Kühl Dec 17 '14 at 15:57
  • Thinking a bit harder about it, `Base::createComponent()` could actually use `Button` and internally just create a `ConcreteButton` - this way the derived classes won't even need to know about this auxiliary class and it can be private in `Base`! I shall update the code correspondingly. – Dietmar Kühl Dec 17 '14 at 15:58
  • You could also instantiate `Concrete` within the `createComponent` to make it even easier :) – maxdev Dec 17 '14 at 15:59
  • Comment race condition :D Nice work, wouldn't have figured that out :) – maxdev Dec 17 '14 at 16:02
2

Access specifier of Button constructor is protected that means it can only be accessed by classes derived from Button. If you want your code to work then you have to make that constructor public.

ravi
  • 10,994
  • 1
  • 18
  • 36
  • Thanks, I thought the inheritance would still work upwards. Found another solution that works too: adding `friend class Component;` to the class body of the `Button`. – maxdev Dec 17 '14 at 15:41
  • Correct. `protected` does not grant access to base classes, only to derived classes... – twalberg Dec 17 '14 at 15:43
  • 2
    @maxdev: Just for the record: you absolutely do **NOT** have to make the constructor `public` and based on what you write you probably don't want to. Although the access right do not work upwards, you can just use a simple delegation through a derived class controlled by the base to deal with the `protected` constructor (see, e.g., my answer). – Dietmar Kühl Dec 17 '14 at 15:46
0

"Button" extends "Component", so "Button" can access protected members of "Component", but "Component" does not know about "Button", and so cannot access it's protected members.

tipaye
  • 454
  • 3
  • 6