Он просто использует магию компилятора. Например, GCC __typeof__
. Для компиляторов, которые не предоставляют такой магии, он предоставляет эмуляцию, которая может определять тип некоторых выражений, но не работает с полностью неизвестными типами.
Возможная реализация может состоять в том, чтобы иметь список функций, которые принимают выражение заданного типа, а затем отправляются из этого типа в число с использованием шаблона класса. Чтобы шаблон функции возвращал число как объект времени компиляции, мы помещаем его в измерение массива.
template<typename> struct type2num;
template<int> struct num2type;
template<typename T> typename type2num<T>::dim &dispatch(T const&);
Затем он переходит от этого числа обратно к типу, чтобы наш EMUL_TYPEOF
мог напрямую назвать тип. Итак, чтобы зарегистрировать тип, мы пишем
#define REGISTER_TYPE(T, N) \
template<> \
struct type2num<T> { \
static int const value = N; \
typedef char dim[N]; \
}; \
template<> \
struct num2type<N> { typedef T type; }
Имея это на месте, вы можете написать
#define EMUL_TYPEOF(E) \
num2type<sizeof dispatch(E)>::type
Всякий раз, когда вам нужно зарегистрировать тип, вы пишете
REGISTER_TYPE(int, 1);
REGISTER_TYPE(unsigned int, 2);
// ...
Конечно, теперь вы обнаружите, что вам нужен механизм для принятия vector<T>
, где вы не знаете T
заранее, а затем это становится произвольно сложным. Вы можете создать систему, в которой числа означают больше, чем просто тип. Вероятно, это может сработать:
#define EMUL_TYPEOF(E) \
build_type<sizeof dispatch_1(E), sizeof dispatch_2(E)>::type
Это может обнаруживать такие типы, как int
, а также такие типы, как shared_ptr<int>
, другими словами, типы, которые не являются специализациями шаблона класса, и специализации шаблона класса с одним аргументом шаблона, выполняя какое-то систематическое сопоставление.
- Если первое число дает 1, второе число определяет тип; в противном случае
- первое число указывает шаблон, а второе число — аргумент шаблона первого типа.
Так что это становится
template<int N, int M>
struct build_type {
typedef typename num2tmp<N>::template apply<
typename num2type<M>::type>::type type;
};
template<int N>
struct build_type<1, N> {
typedef num2type<N>::type type;
};
Нам также нужно изменить шаблон dispatch
и разделить его на две версии, показанные ниже, вместе с REGISTER_TEMP1
для регистрации шаблонов с одним аргументом.
template<typename T> typename type2num<T>::dim1 &dispatch_1(T const&);
template<typename T> typename type2num<T>::dim2 &dispatch_2(T const&);
#define REGISTER_TYPE(T, N) \
template<> \
struct type2num<T> { \
static int const value_dim1 = 1; \
static int const value_dim2 = N; \
typedef char dim1[value_dim1]; \
typedef char dim2[value_dim2]; \
}; \
template<> \
struct num2type<N> { typedef T type; }
#define REGISTER_TMP1(Te, N) \
template<typename T1> \
struct type2num< Te<T1> > { \
static int const value_dim1 = N; \
static int const value_dim2 = type2num<T1>::value_dim2; \
typedef char dim1[value_dim1]; \
typedef char dim2[value_dim2]; \
}; \
template<> struct num2tmp<N> { \
template<typename T1> struct apply { \
typedef Te<T1> type; \
}; \
}
Регистрация шаблона std::vector
и обоих вариантов int
теперь выглядит так
REGISTER_TMP1(std::vector, 2);
// ... REGISTER_TMP1(std::list, 3);
REGISTER_TYPE(int, 1);
REGISTER_TYPE(unsigned int, 2);
// ... REGISTER_TYPE(char, 3);
Вероятно, вы также захотите зарегистрировать несколько чисел для каждого типа, по одному числу для каждой комбинации const/volatile, или вам может понадобиться более одного числа для каждого типа для записи *
, &
и т. д. Вы также хотите поддерживать vector< vector<int> >
, поэтому вам нужно более одного числа для аргумента шаблона, заставляя build_type
вызывать себя рекурсивно. Поскольку вы можете создать произвольный длинный список целых чисел, вы в любом случае можете закодировать что угодно в этой последовательности, так что только ваше творчество в том, как представить эти вещи.
В конце концов, вы, вероятно, повторно реализуете BOOST_TYPEOF :)
person
Johannes Schaub - litb
schedule
26.12.2010
decltype
, что решает проблему. - person jalf   schedule 26.12.2010sizeof()
, и это невозможно, если он четко не обнаружит тип выражение. Это означает, что для предоставления оператораtypeof()
компиляторам не нужно делать дополнительные действия. На самом деле,typeof()
поставляется бесплатно сsizeof()
, так как я думаю, что последний требует больше осмотра/анализа/работы, чем первый. - person Nawaz   schedule 27.12.2010decltype
имеет несколько странных правил, чтобы справиться с этим. Но, во-вторых, получить тип — самая простая часть. Затем вам нужно выяснить, как он должен взаимодействовать с другими частями языка. Вам разрешено это делать:std::vector<typeof(4)>
? Это не бесплатно - person jalf   schedule 27.12.2010sizeof
отбрасывает ссылки. Если вы вызываетеsizeof
для переменной типаint&
, она дает вам размер int. Но должен лиtypeof
для той же переменной возвращатьint
илиint&
? - person jalf   schedule 27.12.2010