Коллега недавно показал мне некоторый код, что нашел онлайн. Это, кажется, позволяет определение времени компиляции того, имеет ли тип, "" отношения с другим типом. Я думаю, что это является полностью потрясающим, но я должен признать, что я невежествен относительно того, как это на самом деле работает. Кто-либо может объяснить это мне?
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;
}
Он объявляет две перегруженные функции с именем test
, одна принимает Base
, а другая - что-нибудь ( ...)
и возвращают разные типы.
Затем он вызывает функцию с Derived
и проверяет размер своего возвращаемого типа, чтобы узнать, какая перегрузка была вызвана. (Он фактически вызывает функцию с возвращаемым значением функции, которая возвращает Derived
, чтобы избежать использования памяти)
Поскольку enum
s являются константами времени компиляции, все это выполняется в системе типов во время компиляции. Поскольку функции не вызываются во время выполнения, не имеет значения, что у них нет тел.
Я не специалист по C ++, но мне кажется, что цель состоит в том, чтобы заставить компилятор выбирать между двумя перегрузками test ()
. Если Производный
получен из Base
, тогда будет использоваться первый, который возвращает char
, в противном случае будет использоваться второй, который возвращает char. [2]
. Затем оператор sizeof ()
определяет, что из этого произошло, и соответственно устанавливает значение конверсия :: существует
.
Кстати, вы можете использовать __ is_base_of
из «type_traits», представленного в std :: tr1
(компилятор MSCV 2008 имеет встроенную поддержку для этого).
Это довольно круто, но на самом деле не работает, потому что определяемое пользователем преобразование предпочтительнее совпадения с многоточием, а ссылка на константу может связывать временный результат определяемое пользователем преобразование. Итак char * p; is_related
вернет true
(проверено в VS2010).
Если вы хотите действительно проверить отношения наследования, можно применить аналогичный подход, но с использованием указателей вместо ссылок.
Есть ли причина, по которой вы бы не использовали вместо этого что-то вроде этого:
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
Таким образом у вас будет доступ к информации во время компиляции.