Странный трюк static_cast? - PullRequest
       6

Странный трюк static_cast?

10 голосов
/ 08 июня 2010

Просматривая исходный код 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 ]

8 голосов
/ 08 июня 2010

Это выглядит как очень сомнительный способ статически утверждать, что параметр шаблона T имеет член Type, а затем проверить, что его значением является ожидаемое магическое число, как вы утверждаете, что должны делать.

Так как Type является значением перечисления, указатель this не требуется для доступа к нему, поэтому static_cast<Item>(0)->Type извлекает значение Item::Type без фактического использования значения указателя.Так что это работает, но, возможно, это неопределенное поведение (в зависимости от вашего взгляда на стандарт, но в любом случае IMO - плохая идея), потому что код разыменовывает указатель NULL с помощью оператора разыменования указателя (->).Но я не могу понять, почему это лучше, чем Item::Type или шаблон T::Type - возможно, это устаревший код, предназначенный для работы на старых компиляторах с плохой поддержкой шаблонов, который не может понять, что должно означать T::Type.

Тем не менее, конечным результатом является код, такой как qgraphicsitem_cast<bool>(ptr), не выполнится во время компиляции , поскольку bool не имеет Type перечисления членов.Это более надежно и дешевле, чем проверки во время выполнения, даже если код выглядит как взлом.

5 голосов
/ 08 июня 2010

Это немного странно, да, и это официально неопределенное поведение.

Возможно, они могли бы написать это следующим образом (обратите внимание, что здесь 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 версии одной и той же функции. Может быть, причина сделанного ими выбора.

1 голос
/ 27 июня 2011

Это обычная уловка для использования защищенных (статических) членов вне (под) класса.Лучшим способом было бы представить какой-нибудь метод, но, поскольку он не предназначен для использования в качестве пользовательского класса, они отпустили тяжелую работу?

1 голос
/ 08 июня 2010

Текущий стандарт и черновик для следующего стандарта предполагают, что разыменование нулевого указателя является неопределенным поведением (см. Раздел 1.9). Поскольку a->b является ярлыком для (*a).b, код выглядит , как будто он пытается разыменовать нулевой указатель. Интересный вопрос: действительно ли static_cast<T>(0)->Type представляет собой разыменование нулевого указателя?

В случае, если Type был членом данных, это определенно разыменовывало бы нулевой указатель и, таким образом, вызывало неопределенное поведение. Но согласно вашему коду Type - это просто значение enum, а static_cast<T>(0)-> используется только для поиска по области / имени. В лучшем случае этот код сомнителен. Меня раздражает, что «свойство статического типа», такое как локальное значение перечисления, доступно через оператор стрелки. Я, наверное, решил бы это по-другому:

typedef typename remove_pointer<T>::type pointeeT;
return … pointeeT::Type … ;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...