Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bizarre static_cast trick?

While perusing the Qt source code I came across this gem:

template <class T> inline T qgraphicsitem_cast(const QGraphicsItem *item)
{
    return int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type)
        || (item && int(static_cast<T>(0)->Type) == item->type()) ? static_cast<T>(item) : 0;
}

Notice the static_cast<T>(0)->Type? I've been using C++ for many years but have never seen 0 being used in a static_cast before. What is this code doing and is it safe?

Background: If you derive from QGraphicsItem you are meant to declare an unique enum value called Type that and implement a virtual function called type that returns it, e.g.:

class Item : public QGraphicsItem
{
public:
  enum { Type = MAGIC_NUMBER };
  int type() const { return Type; }
  ...
};

You can then do this:

QGraphicsItem* item = new Item;
...
Item* derivedItem = qgraphicsitem_cast<Item*>(item);

This will probably help explain what that static_cast is trying to do.

like image 837
Rob Avatar asked Jun 08 '10 08:06

Rob


People also ask

Is static cast Safe?

static_cast conversions are not as safe as dynamic_cast conversions, because static_cast does no run-time type check, while dynamic_cast does. A dynamic_cast to an ambiguous pointer will fail, while a static_cast returns as if nothing were wrong; this can be dangerous.

Can static_cast fail?

static_cast can't throw exception since static_cast is not runtime cast, if some cannot be casted, code will not compiles. But if it compiles and cast is bad - result is undefined.

How does static_cast int work?

The static_cast operator converts variable j to type float . This allows the compiler to generate a division with an answer of type float . All static_cast operators resolve at compile time and do not remove any const or volatile modifiers.


1 Answers

This looks like a very dubious way to statically assert that the template parameter T has a Type member, and then verify its value is the expected magic number, like you state you are supposed to do.

Since Type is an enum value, the this pointer is not required to access it, so static_cast<Item>(0)->Type retrieves the value of Item::Type without actually using the value of the pointer. So this works, but is possibly undefined behavior (depending on your view of the standard, but IMO a bad idea anyway), because the code dereferences a NULL pointer with the pointer dereference operator (->). But I can't think why this is better over just Item::Type or the template T::Type - perhaps it's legacy code designed to work on old compilers with poor template support that couldn't work out what T::Type is supposed to mean.

Still, the end result is code such as qgraphicsitem_cast<bool>(ptr) will fail at compile time because bool has no Type member enum. This is more reliable and cheaper than runtime checks, even if the code looks like a hack.

like image 133
AshleysBrain Avatar answered Sep 23 '22 23:09

AshleysBrain