Как QList решает, следует ли хранить указатели или сами элементы?

Из документов:

Внутренне QList представлен как массив указателей на элементы типа T. Если T сам по себе является типом указателя или базовым типом, который не больше указателя, или если T является одним из общих классов Qt, то QList хранит элементы непосредственно в массиве указателей.

Мне интересно узнать, как QList «решает», хранить ли указатели или сами элементы в зависимости от типа?

Они делают это, используя какой-то эзотерический шаблонный синтаксис?


person sashoalm    schedule 08.07.2013    source источник
comment
поскольку вы определяете свой список как: QList<int> или QList<int*> или даже QList<myBigStructureTypeThingy>, вы сообщаете QList, какой тип вы будете хранить. Если этот тип по размеру равен или меньше указателя, он будет хранить всю переменную, в противном случае массив поиска будет состоять только из указателей на переменные. Сам список тогда будет состоять из массива поиска и области памяти данных (если QList‹NoPointerType›).   -  person Sebastian Lange    schedule 08.07.2013


Ответы (1)


Давайте прочитаем исходник (я использую Qt 4.8).

qlist.h:

struct Node { 
    void *v;
#if defined(Q_CC_BOR)
    Q_INLINE_TEMPLATE T &t();
#else
    Q_INLINE_TEMPLATE T &t()
    { return *reinterpret_cast<T*>(QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic
                                   ? v : this); }
#endif
};

QList использует QTypeInfo для определения свойств типа. QTypeInfo объявляется для разных типов отдельно. В qglobal.h:

Объявление по умолчанию для неизвестного типа:

template <typename T>
class QTypeInfo
{
public:
    enum {
        isPointer = false,
        isComplex = true,
        isStatic = true,
        isLarge = (sizeof(T)>sizeof(void*)),
        isDummy = false
    };
};

Объявление для указателей:

template <typename T>
class QTypeInfo<T*>
{
public:
    enum {
        isPointer = true,
        isComplex = false,
        isStatic = false,
        isLarge = false,
        isDummy = false
    };
};

Макросы для удобного объявления информации о типе:

#define Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS) \
class QTypeInfo<TYPE > \
{ \
public: \
    enum { \
        isComplex = (((FLAGS) & Q_PRIMITIVE_TYPE) == 0), \
        isStatic = (((FLAGS) & (Q_MOVABLE_TYPE | Q_PRIMITIVE_TYPE)) == 0), \
        isLarge = (sizeof(TYPE)>sizeof(void*)), \
        isPointer = false, \
        isDummy = (((FLAGS) & Q_DUMMY_TYPE) != 0) \
    }; \
    static inline const char *name() { return #TYPE; } \
}

#define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \
template<> \
Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS)

Объявление для примитивных типов:

Q_DECLARE_TYPEINFO(bool, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(char, Q_PRIMITIVE_TYPE); /* ... */

Также он определен для определенных типов Qt, например. в qurl.h:

Q_DECLARE_TYPEINFO(QUrl, Q_MOVABLE_TYPE);

Или в qhash.h:

Q_DECLARE_TYPEINFO(QHashDummyValue, Q_MOVABLE_TYPE | Q_DUMMY_TYPE);

Когда QList использует QTypeInfo<T>, компилятор выбирает ближайшее из существующих определений QTypeInfo.

person Pavel Strakhov    schedule 08.07.2013
comment
Итак, Node::t() — это функция, которая творит чудеса. В зависимости от QTypeInfo он разыменовывает либо сохраненное 32-битное значение, либо сам указатель this. Спасибо за понимание! - person sashoalm; 08.07.2013