Определение типа времени компиляции в C++

Коллега недавно показал мне некоторый код, что нашел онлайн. Это, кажется, позволяет определение времени компиляции того, имеет ли тип, "" отношения с другим типом. Я думаю, что это является полностью потрясающим, но я должен признать, что я невежествен относительно того, как это на самом деле работает. Кто-либо может объяснить это мне?

template<typename BaseT, typename DerivedT>
inline bool isRelated(const DerivedT&)
{
    DerivedT derived();
    char test(const BaseT&); // sizeof(test()) == sizeof(char)
    char (&test(...))[2];    // sizeof(test()) == sizeof(char[2])
    struct conversion 
    { 
        enum { exists = (sizeof(test(derived())) == sizeof(char)) }; 
    };
    return conversion::exists;
} 

После того как эта функция определяется, можно использовать ее как это:

#include <iostream>

class base {};
class derived : public base {};
class unrelated {};

int main()
{
    base b;
    derived d;
    unrelated u;

    if( isRelated<base>( b ) )
        std::cout << "b is related to base" << std::endl;

    if( isRelated<base>( d ) )
        std::cout << "d is related to base" << std::endl;

    if( !isRelated<base>( u ) )
        std::cout << "u is not related to base" << std::endl;
} 
16
задан Georg Fritzsche 13 April 2010 в 23:43
поделиться

5 ответов

Он объявляет две перегруженные функции с именем test , одна принимает Base , а другая - что-нибудь ( ...) и возвращают разные типы.

Затем он вызывает функцию с Derived и проверяет размер своего возвращаемого типа, чтобы узнать, какая перегрузка была вызвана. (Он фактически вызывает функцию с возвращаемым значением функции, которая возвращает Derived , чтобы избежать использования памяти)

Поскольку enum s являются константами времени компиляции, все это выполняется в системе типов во время компиляции. Поскольку функции не вызываются во время выполнения, не имеет значения, что у них нет тел.

11
ответ дан 30 November 2019 в 17:52
поделиться

Я не специалист по C ++, но мне кажется, что цель состоит в том, чтобы заставить компилятор выбирать между двумя перегрузками test () . Если Производный получен из Base , тогда будет использоваться первый, который возвращает char , в противном случае будет использоваться второй, который возвращает char. [2] . Затем оператор sizeof () определяет, что из этого произошло, и соответственно устанавливает значение конверсия :: существует .

6
ответ дан 30 November 2019 в 17:52
поделиться

Кстати, вы можете использовать __ is_base_of из «type_traits», представленного в std :: tr1 (компилятор MSCV 2008 имеет встроенную поддержку для этого).

2
ответ дан 30 November 2019 в 17:52
поделиться

Это довольно круто, но на самом деле не работает, потому что определяемое пользователем преобразование предпочтительнее совпадения с многоточием, а ссылка на константу может связывать временный результат определяемое пользователем преобразование. Итак char * p; is_related (p); вернет true (проверено в VS2010).

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

5
ответ дан 30 November 2019 в 17:52
поделиться

Есть ли причина, по которой вы бы не использовали вместо этого что-то вроде этого:

template<typename BaseT, typename DerivedT>
struct IsRelated
{
    static DerivedT derived();
    static char test(const BaseT&); // sizeof(test()) == sizeof(char)
    static char (&test(...))[2];    // sizeof(test()) == sizeof(char[2])

    enum { exists = (sizeof(test(derived())) == sizeof(char)) }; 
}

?

например:

IsRelated<Base, Derived>::exists

Таким образом у вас будет доступ к информации во время компиляции.

5
ответ дан 30 November 2019 в 17:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: