奇怪的 static_cast 技巧?

发布于 2024-09-04 06:42:36 字数 896 浏览 9 评论 0原文

在仔细阅读 Qt 源代码时,我遇到了这个 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;
}

注意到 static_cast(0)->Type?我使用 C++ 已经很多年了,但以前从未见过在 static_cast 中使用 0 。这段代码在做什么并且安全吗?

背景:如果您从 QGraphicsItem 派生,则需要声明一个名为 Type 的唯一枚举值,并实现一个名为 type 的虚拟函数来返回它,例如:

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

然后您可以这样做:

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

这可能有助于解释 static_cast 试图做什么。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

甜心小果奶 2024-09-11 06:42:36

这看起来像是一种非常可疑的方式来静态断言模板参数 T 具有 Type 成员,然后验证其值是预期的幻数,就像您声明的那样应该做的。

由于 Type 是一个枚举值,因此不需要 this 指针来访问它,因此 static_cast(0)->Type检索 Item::Type 的值,而不实际使用指针的值。所以这有效,但可能是未定义的行为(取决于您对标准的看法,但无论如何,IMO 都是一个坏主意),因为代码使用指针取消引用运算符取消对 NULL 指针的引用(->) 。但我不明白为什么这比 Item::Type 或模板 T::Type 更好 - 也许它是设计用于在性能较差的旧编译器上工作的遗留代码模板支持无法弄清楚 T::Type 的含义。

尽管如此,最终结果是诸如 qgraphicsitem_cast(ptr) 之类的代码将在编译时失败,因为 bool 没有 Type 成员枚举。这比运行时检查更可靠、更便宜,即使代码看起来像是黑客攻击。

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.

故事灯 2024-09-11 06:42:36

是的,这有点奇怪,并且是官方未定义的行为。

也许他们可以这样写(注意,这里的 T 不再是指针,无论它在原始代码中是否是指针):

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

但他们可能已经被常量所困扰,并被迫编写同一函数的 2 个版本。也许是他们做出选择的原因。

It's a bit strange, yes, and is officially undefined behavior.

Maybe they could have written it as follows (note that T here is no more a pointer, whether it is a pointer in the original code):

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

But they may have been bitten by constness, and forced to write 2 versions of the same function. Maybe a reason for the choice they made.

聽兲甴掵 2024-09-11 06:42:36

当前标准和即将推出的标准草案表明取消引用空指针是未定义的行为(请参阅第 1.9 节)。由于 a->b(*a).b 的快捷方式,因此代码看起来就像尝试取消引用空指针。这里有趣的问题是:static_cast(0)->Type实际上是否构成了空指针取消引用?

如果 Type 是一个数据成员,这肯定会取消引用空指针,从而调用未定义的行为。但根据您的代码 Type 只是一个枚举值,而 static_cast(0)-> 仅用于范围/名称查找。这段代码充其量是有问题的。我发现通过箭头运算符访问“静态类型属性”(例如本地枚举值)很烦人。我可能会以不同的方式解决它:

typedef typename remove_pointer<T>::type pointeeT;
return … pointeeT::Type … ;

The current standard and the draft for the upcoming standard suggest that dereferencing a null pointer is undefined behaviour (see section 1.9). Since a->b is a shortcut for (*a).b the code looks like it tries to dereference a nullpointer. The interesting question here is: Does static_cast<T>(0)->Type actually constitute a null pointer dereference?

In case Type was a data member this would definitely be dereferencing a nullpointer and thus invoke undefined behaviour. But according to your code Type is just an enum value and static_cast<T>(0)-> is only used for scoping / name lookup. At best this code is questionable. I find it irritating that a "static type property" like a local enum value is accessed via the arrow operator. I probably would have solved it differently:

typedef typename remove_pointer<T>::type pointeeT;
return … pointeeT::Type … ;
断舍离 2024-09-11 06:42:36

这是从(子)类外部使用受保护(静态)成员的常见技巧。更好的方法是公开一些方法,但由于它不是为了成为用户类,所以他们放弃了艰苦的工作?!

This is a common trick to use protected(static) members from outside the (sub)class. A better way would have been to expose some method, but since it wasn't intended to be user class they let go the hard work?!!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文