0

I am wondering about a short code example of an application of downcast via static_cast, under conditions where there is no undefined behaviour. I have looked around and I found quite a few texts (posts, Q&A, etc.) referring to fragments of the standard, explaining, etc. But I found no examples that illustrate that (and that surprises me).

Could anyone provide one such example?

  • When the logic of the program ensures that the `static_cast` as a downcast is correct. The classical example is the CRTP answer below. – alfC Feb 02 '22 at 07:49

2 Answers2

5

A downcast via static_cast is part of the typical implementation of the Curiously recurring template pattern (CRTP). The example from wikipedia:

template <class T> 
struct Base
{
    void interface()
    {
        // ...
        static_cast<T*>(this)->implementation();
        // ...
    }

    static void static_func()
    {
        // ...
        T::static_sub_func();
        // ...
    }
};

struct Derived : Base<Derived>
{
    void implementation();
    static void static_sub_func();
};

Base "knows" that it can downcast this to T* via static_cast (rather than dynamic_cast) because the whole purpose of Base is to be inherited by T. For more details on CRTP I refer you to the wikipedia article.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Good example of application for a programming technique. – sancho.s ReinstateMonicaCellio Feb 02 '22 at 08:21
  • @sancho.sReinstateMonicaCellio actually it is difficult to find examples that don't smell. The other answer has a good example, but I would argue that it needs a review and then refactoring. Imho it is a good intuition that downcasts aren't nice and should be done via `dynamic_cast` if at all. – 463035818_is_not_an_ai Feb 02 '22 at 08:28
  • Both answers were good, and they helped me better understanding what good uses could `static_cast` have. This helped me on my way (I'm still not there, and I may have further questions) to fixing a code I received that uses `static_cast` leading to UB. Unfortunately, that code is too entangled to put together a reasonable MCVE that represents it (at this point). – sancho.s ReinstateMonicaCellio Feb 02 '22 at 09:00
2

A very basic example:

class Animal {};
class Dog : public Animal {};

void doSomething() {
  Animal *a = new Dog();
  Dog *d = static_cast<Dog*>(a);
}

A more contrived example:

class A { public: int a; };
class B : public A {};
class C : public A {};
class D : public B, public C {};

void doSomething() {
  D *d = new D();
  int bValue = static_cast<B*>(d)->a;
  int cValue = static_cast<C*>(d)->a;

  // This does not work because we have two a members in D!
  // int eitherValue = d->a;
}

There are also countless other cases where you know the actual type type of the instance due to some flags or invariants in your program. However, many sources recommend preferring dynamic_cast in all cases to avoid errors.

idmean
  • 14,540
  • 9
  • 54
  • 83