0

I want to have a static member a of a base class B, with the following features:

  1. It is a vector (likely not the "challenging" part).
  2. Derived classes D1, etc., should be able to have similar static members, with different values. Not all the classes would have them, and if a class D2 does not define it, its corresponding a would be the lowest in the parent hierarchy.
  3. I want to access any of these members in any of the classes with a single function getA defined in the base class. E.g., D1 should be able to get its own a (or the lowest in the parent hierarchy, if D1 does not define it).

So far, the solutions I have seen require redefining getA in each derived class in the hierarchy (see below). I want to avoid this for several reasons, e.g., I have a large hierarchy, so applying the required changes, and possible future changes (even if unlikely) become cumbersome.

Are there any alternatives to achieve my goal?

Related:

  1. Overriding static variables when subclassing
  2. https://forums.unrealengine.com/t/workaround-overriding-a-static-variable/91685/2
  • 3
    They can be shadowed. They cannot be overridden. – Eljay Jul 28 '22 at 22:44
  • Hmm, I feel like there should be a question like that, but I don't know how to search for it. Basically, you could implement something like vtable - `B` has non-static member `std::vector*` and a constructor that requires initialization of that, derived classes have `static std::vector` and give an address to that when initilizing `B`. – Yksisarvinen Jul 28 '22 at 22:44
  • 1
    Why not a simple (non-static) virtual getter and the static var in that? You have an instance anyways. – lorro Jul 28 '22 at 22:47
  • Also, if you want to pass the type of most derived class to some base policy (several levels deeper), likely you'd like to give virtual inheritance a try. A virtually inherited class ctor must be called from the most derived class and, if it's a template ptr, you can simpy write: `Derived() : Policy(this) {}`. – lorro Jul 28 '22 at 22:49
  • And the problem with having `getA` be a virtual function, implemented in each subclass would be what, exactly? I can think of others designs, but none of them are as simple this (can't get any simpler), and they all have more complexity and drawbacks. – Sam Varshavchik Jul 28 '22 at 22:50
  • @SamVarshavchik Hmm, when going into implementing it, I understood what you meant I think: design indeed is not simple without repeating or macros. – lorro Jul 28 '22 at 22:55
  • 1
    Nobody has ever accused C++ of being easy, simple, and straightforward, @lorro. – Sam Varshavchik Jul 28 '22 at 22:58
  • That said, you might still put the implementation of `getA() const override` to a CRTP base class... – lorro Jul 28 '22 at 23:00
  • @SamVarshavchik - As stated in the question, adding these static members to the already existing code, and perhaps modifying their access in the future becomes cumbersome. If there are no options to achieve my goal, of course I would have to face the task, but asking if there is a simpler way is a valid question, I guess... the answer may be "no, there isn't". – sancho.s ReinstateMonicaCellio Jul 28 '22 at 23:02
  • Well, if you're asking -- is there a way to do this automatically simply by the virtue of defining a subclass and its static class member -- no, this is not possible in C++. You have to define something that has virtual inheritance, or which simulates one, in some form or fashion. C++ does not work in any other way. – Sam Varshavchik Jul 28 '22 at 23:07
  • @SamVarshavchik - I was not bound to that strict way of putting it. I was open to alternatives... in the end, I am asking because I don't know. – sancho.s ReinstateMonicaCellio Jul 28 '22 at 23:15
  • Well, the simplest alternative is what everyone already said: a virtual getter. The only simpler way would be to have everything happen automatically, somehow, but that's not going to happen. You have to declare a static member in a subclass, you know you have to do that. That, an absolute minimum, by itself, is not going to accomplish anything more than that. So, just one more step: define the virtual method in the subclass. Abracadabra: the one in the base class gets overriden, automatically, and `getA` then does what you wanted it to do. Can't get any simpler than that. – Sam Varshavchik Jul 28 '22 at 23:17

2 Answers2

1

No, you can't override member variables (static and non-static alike) - only virtual member functions.

the solutions I have seen require redefining getA in each derived class

You would only need to override getA in the classes that should have a separate a.

Example:

struct B {
    virtual std::vector<int>& getA() const { 
        static std::vector<int> a;
        return a; 
    }
};

struct D1 : B {
    std::vector<int>& getA() const override {
        static std::vector<int> a;
        return a; 
    }    
};

struct D2 : B { // no need to override `getA` here - it'll use `B::getA`
};

Demo

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • This is what I was thinking about first, but user noted that they already have the code with the static variable somewhere. – lorro Jul 28 '22 at 23:29
  • @lorro Yeah, but moving it into a member function shouldn't be so much work so I thought it was worth mentioning. It's also makes it a little cleaner i.m.o. since you now can't access the `static` variable directly. – Ted Lyngmo Jul 28 '22 at 23:33
  • Except that you can access the static variable from static functions of the derived classes. User didn't rule out this use case. If you implement a derived class, you're aware of its members, therefore it's ok to refer to that specific member's static; but you can't do that if it's instance-bound. Note, it's only a small nit-picking: you can move the static vector out of `getA()` and then it's ok. – lorro Jul 28 '22 at 23:42
  • @lorro True. OP can keep the `static` variables at class scope if direct access is actually needed - or move them into the override-able member functions to only provide access via dynamic dispatch. Since OP wanted overrideable member variables I find it unlikely that direct access without an instance is actually a requirement. – Ted Lyngmo Jul 28 '22 at 23:44
0

How about having a simple (non-static) virtual getter and the static var in that getter? As you said, you already have an instance, so you have access to the virtual table. To make it automatic, we might rely on the typical CRTP:

class Base {
public:
    virtual const std::vector<int>& getA() = 0;
};

template<typename B, typename D>
class BaseImpl : public B {
public:
    const std::vector<int>& getA() override { return D::static_var; };
};

class D1 : public BaseImpl<Base, D1>
{
public:
    static const std::vector<int> static_var;
};

const std::vector<int> D1::static_var = {1};

class D2 : public D1 /* no static_var */
{
};

class D3 : public BaseImpl<D2, D3>
{
public:
    static const std::vector<int> static_var;
};

const std::vector<int> D3::static_var = {1, 3};

You might even automate the detection of static_var if that's easier for you (via std::enable_if<> and is_detected<>).

lorro
  • 10,687
  • 23
  • 36
  • `BaseImpl` overrides `getA` but does not inherit from `Base`. I thought that was a typo, but you later inherit from both `Base` and `BaseImpl` in `D1` and `D3` so I think more needs fixing. The use of `constexpr` looks fishy too. – Ted Lyngmo Jul 28 '22 at 23:20
  • @TedLyngmo Perhaps we can call it `BaseOverride`. In the model, we have `Base` -> `D1` -> `D2` -> `D3` as the main inheritance chain; then, we have overrides of the static var in `D1` and `D3`. Hence, in `D2` and `D3` we don't inherit from `Base`. Also, `constexpr std::vector` is a thing in recent C++ (I normally 'think' in immutale codes, so when something is static, it's very likely also constexpr unless it depends on the actual environment); but you might consider a non-constexpr version as well. – lorro Jul 28 '22 at 23:28
  • Hmm, I don't see how it could work. Can you compile this in compiler explorer? – Ted Lyngmo Jul 28 '22 at 23:33
  • I reworked it a bit. [This](https://godbolt.org/z/WKaKjq77n) would work – Ted Lyngmo Jul 28 '22 at 23:48
  • @TedLyngmo you're right, I need to include the base class in the template arguments to make this work. Also, I've removed `constexpr` so that it's compatible with older compilers. – lorro Jul 28 '22 at 23:48
  • @TedLyngmo Thanks for the rework, but the idea in this answer was not that D1, D2 and D3 are direct descendants of Base, but that they descend from each other (to show the override in deep inheritance hierarchy). Edited the post based on your comment, hope it's better now. – lorro Jul 28 '22 at 23:50
  • I didn't think of `constexpr` being a problem for older compilers but that it also makes them `const` which I just assumed wasn't wanted. I was about to suggest `inline` instead. It now looks compileable but I'm not sure about the CRTP variant in this. `BaseImpl`, inheritance from `B` and access to the variable via `D`. It feels messy but I may just need some time to let it sink in :-) – Ted Lyngmo Jul 29 '22 at 00:02
  • @TedLyngmo Think of it as `class X : public TemplateClass<..., X>` first, then it's (a bit) less ugly. Not saying I wouldn't like the static constructor proposal voted in :). – lorro Jul 29 '22 at 00:05