Определение типа времени компиляции в C ++ - PullRequest
16 голосов
/ 14 апреля 2010

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

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;
} 

Ответы [ 6 ]

11 голосов
/ 14 апреля 2010

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

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

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

6 голосов
/ 14 апреля 2010

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

5 голосов
/ 14 апреля 2010

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

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 голосов
/ 14 апреля 2010

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

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

2 голосов
/ 14 апреля 2010

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

1 голос
/ 08 марта 2013

Исходный код создаст объект из Derived, это может привести к неожиданному результату. Ниже работы может быть альтернативный выбор:

template<typename BaseT, typename CheckT>
inline  bool    isDerived(const CheckT &t){
    char    test(const BaseT   *t);
    char    (&test(...))[2];
    return  (sizeof(test(&t)) == sizeof(char) );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...