OlegG
OlegG

Reputation: 1017

C++ static const members overriding

Consider the following.

struct A {
    static const int X = 1;
    static void printX() {std::cout << "X " << X << std::endl; };
};

struct B: public A {
    static const int X = 2;
};

int main(argc argv){
    B b;
    b.printX();
}

How to force the b.printX() to print value 2?
Both - constant and method - MUST be static. And therefore, virtual methods are not suitable.

For anyone who thinks they know my task better than me and wants to see me rethinking it, I'll explain the goal of my efforts :)
Just imagine class which has behaviour based on set of static constants. The simplest way to implement child class with different set of constants and therefore with different behaviour is derivation of class from previous one with specific set of constant values. It is possible to solve that task using virtual functions. Of cause possible, no question. But this solution will be not very pure in sense of accordance to a theory of modeled entities. Usage of virtual methods in this case will be more a trick than correct implementation.
For example, IR channels have different timing of pulse durations and package structure. It is convenient to define set of child classes (different IR channel implementations) with a specific set of constant values. That values are static because they are common for every object of class and const because they are needed at compile time only. And because internal implementations of base and child classes are slightly different the best relationship between of them are super class - child class.
Is it rationale for my original question now?

Upvotes: 15

Views: 15918

Answers (7)

baz
baz

Reputation: 1587

Why not using templates?

constexpr int nfo[]{ 0, 1, 2, 3 };

template <int N>
class Haha {
public:
        const static int BOOM = nfo[ N ];
};
int main() {
        std::cout << Haha<2>::BOOM << std::endl; // 2
}

Upvotes: 1

rnstlr
rnstlr

Reputation: 1803

Just make the value of X a template parameter:

#include <iostream>

template<int XValue=1>
struct A {
        static const int X = XValue;
        static void printX() {std::cout << "X " << X << std::endl; };
};

template<int XValue=2>
struct B: public A<XValue> {
};

struct C: public B<3> {
};

int main(int, char**){
        B<> b;
        b.printX();
}

Upvotes: 6

Andriy Makukha
Andriy Makukha

Reputation: 8304

Instead of using static and template, I would just use regular constant attributes and constructors.

For example:

#include <iostream>

struct A {
    A(const char* fn, const int X) : filename(fn), X(X) {};
    void print() { std::cout << "X = " << X << ", FN = " << filename << std::endl; };

  protected:
    const char* filename;
    const int X;
};

struct B : public A {
    B() : A("data.dat", 5) {};
};

int main(int, char **) {
    B b;
    b.print();
}

Functionally, it does exactly what you want. Output:

X = 5, FN = data.dat

— It's now the compiler's job to optimize those constants. And if you are not intending to use thousands of objects B, it's probably not worth worrying about making those constants static.

Upvotes: 3

Jive Dadson
Jive Dadson

Reputation: 17026

You are going to need a template, and to change the inheritance to use the template, as you will see. The trick is to make it work whether the derived class has an X to overshadow the base-class X or not.

template<class C>
struct A {
    static const int X = 1;
    template<typename T>
    static int getX(decltype(T::X)*) 
        { return T::X; }
    template<typename T>
    static void getX(...)
        { return X; }
    static void printX()
        { std::cout << "X " << getX<C>(0) << std::endl; }
};

struct B: public A<B> {
    static const int X = 2;
};

struct B2: public A<B2> {
    // No X
};

int main(){
    B b;
    b.printX(); // Prints X 2
    B2 b2;
    b2.printX(); // Prints X 1
}

Upvotes: 9

Nik Bougalis
Nik Bougalis

Reputation: 10613

OK, I'll play along... you want to nest this more than one level deep. Fine.

#include <iostream>

template <int XX> struct T
{
    static const int X = XX;

    static void printX()
    {
        std::cout << "X=" << X << std::endl;
    }   
};

struct AA 
{
    static const int X = 1;

    /* all other members go here */
};

struct A : public AA, public T<AA::X>
{
    /* empty - put stuff in AA instead */
};

struct BB : public AA
{
    static const int X = 2;
};

struct B : public BB, public T<BB::X>
{
};

struct CC : public BB
{
    static const int X = 3;
};

struct C : public CC, public T<CC::X>
{
};

struct DD : public CC
{
    static const int X = 4;
};

struct D : public DD, public T<DD::X>
{
};

int main(int, char **)
{
    A a;
    B b;
    C c;
    D d;

    a.printX();
    b.printX();
    c.printX();
    d.printX();

    return 0;
}

You could even skip the static const int X = ...; in every class, and just do public T<1>, public T<2> etc as necessary.

Upvotes: 0

Nik Bougalis
Nik Bougalis

Reputation: 10613

Short answer: you can't.

Slightly longer, more complex answer: Well, maybe you can. With templates!

#include <iostream>

template <typename T> struct A 
{
    static const int X = 1;

    static void printX() 
    { 
        std::cout << "X=" << T::X << std::endl; 
    }
};

struct B : public A<B> 
{
    static const int X = 2;
};

int main(int, char **)
{
    B b;

    b.printX();

    return 0;
}

Upvotes: 0

Michael Aaron Safyan
Michael Aaron Safyan

Reputation: 95509

By definition, anything you do with static members will be "overshadowing", not "overriding". You could reimplement "printX()" in "B", but you would not really be overriding the behavior; because this would use overshadowing, the behavior would depend entirely on the compile-time, not runtime, type.

Upvotes: 4

Related Questions