Подтвердить что ты не робот

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

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

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

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 6

Исходный код построит объект 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) );
}