在仔细阅读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正在尝试做什么。
答案 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)
这是在(子)类之外使用受保护(静态)成员的常见技巧。一种更好的方法是暴露一些方法,但由于它不打算成为用户类,他们放弃了努力工作?!!