代码之家  ›  专栏  ›  技术社区  ›  Rob

奇怪的静态戏法?

  •  10
  • Rob  · 技术社区  · 14 年前

    在阅读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被用于静态统计。这个代码在做什么,它安全吗?

    背景:如果你从 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);
    

    这可能有助于解释静态演员表正在尝试做什么。

    4 回复  |  直到 13 年前
        1
  •  8
  •   AshleysBrain    14 年前

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

    自从 类型 是枚举值, this 指针不需要访问它,所以 static_cast<Item>(0)->Type 检索的值 Item::Type 不需要实际使用指针的值。所以这是可行的,但可能是未定义的行为(取决于您对标准的看法,但我还是个坏主意),因为代码使用指针取消引用运算符取消对空指针的引用( -> )但我想不出为什么这比 项目类型: 或者模板 T::Type -可能是设计用于旧编译器的旧代码,模板支持不佳,无法计算出 类型: 应该是说。

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

        2
  •  5
  •   Didier Trosset    14 年前

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

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

    但它们可能被常量咬了,被迫编写同一函数的两个版本。也许是他们做出选择的原因。

        3
  •  1
  •   sellibitze    14 年前

    当前标准和即将出台的标准草案表明,取消对空指针的引用是未定义的行为(见第1.9节)。自从 a->b 是的快捷方式 (*a).b 代码 就像它试图取消对空指针的引用一样。这里有趣的问题是:是吗? static_cast<T>(0)->Type 是否实际构成空指针取消引用?

    万一 Type 是一个数据成员,这肯定会取消对空指针的引用,从而调用未定义的行为。但是根据你的密码 类型 只是一个枚举值并且 static_cast<T>(0)-> 仅用于范围/名称查找。这个代码充其量是有问题的。我发现通过arrow运算符访问“static type属性”(如本地枚举值)令人恼火。我可能会用不同的方法来解决:

    typedef typename remove_pointer<T>::type pointeeT;
    return … pointeeT::Type … ;
    
        4
  •  1
  •   Pkj    13 年前

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