10

在阅读 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<T>(0)->Type? 我已经使用 C++ 很多年了,但之前从未见过 0 在 static_cast 中使用过。这段代码在做什么,它安全吗?

背景:如果您从中派生,QGraphicsItem则意味着声明一个名为Typethat 的唯一枚举值并实现一个称为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 试图做什么。

4

4 回答 4

8

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

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

尽管如此,最终的结果还是会因为没有成员枚举而在编译时qgraphicsitem_cast<bool>(ptr)失败。这比运行时检查更可靠、更便宜,即使代码看起来像 hack。boolType

于 2010-06-08T10:36:46.590 回答
5

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

也许他们可以这样写(注意这里的 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;
}

但是他们可能已经被 constness 咬住了,被迫编写了同一个函数的 2 个版本。也许是他们做出选择的原因。

于 2010-06-08T08:41:27.793 回答
1

当前标准和即将发布的标准草案表明,取消引用空指针是未定义的行为(参见第 1.9 节)。因为a->b(*a).b代码的快捷方式,看起来它试图取消引用空指针。这里有趣的问题是:static_cast<T>(0)->Type实际上是否构成了空指针解引用?

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

typedef typename remove_pointer<T>::type pointeeT;
return … pointeeT::Type … ;
于 2010-06-08T15:08:39.600 回答
1

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

于 2011-06-27T17:08:11.720 回答