它只是使用编译器魔法。比如,GCC 的__typeof__。对于不提供这种魔法的编译器,它提供了一个模拟,可以检测 some 表达式的类型,但会因完全未知的类型而失败。
一个可能的实现可能是拥有一个接受给定类型表达式的函数列表,然后使用类模板从该类型分派到一个数字。为了使函数模板以编译时实体的形式返回数字,我们将其放入数组维度中
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 :)