奇怪的static_cast技巧?

时间:2010-06-08 08:32:06

标签: c++ qt

在仔细阅读Qt源代码时,我遇到了这个宝石:

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,你应该声明一个名为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正在尝试做什么。

4 个答案:

答案 0 :(得分:8)

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

由于Type是枚举值,因此this指针不需要访问它,因此static_cast<Item>(0)->Type检索Item::Type的值而不实际使用值->指针。所以这可行,但可能是未定义的行为(取决于您对标准的看法,但IMO无论如何都是一个坏主意),因为代码使用指针解除引用运算符(Item::Type)取消引用NULL指针。但我想不出为什么这比仅T::Type或模板T::Type更好 - 也许它的遗留代码设计用于那些模板支持较差的旧编译器无法解决{{1}应该是指。

但最终结果是qgraphicsitem_cast<bool>(ptr)等代码在编译时将失败,因为bool没有Type成员枚举。这比运行时检查更可靠,更便宜,即使代码看起来像黑客。

答案 1 :(得分: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 :(得分:1)

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

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

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

答案 3 :(得分:1)

这是在(子)类之外使用受保护(静态)成员的常见技巧。一种更好的方法是暴露一些方法,但由于它不打算成为用户类,他们放弃了努力工作?!!