Naw*_*waz 7 c++ templates boost metaprogramming typeof
我只是想知道boost如何实现BOOST_TYPEOF(在C++ 03中),这似乎是一个非常有用的工具.任何人有任何想法?
另外,我想C++ 03本身也有提供typeof
运营商,尤其是当它已经具有sizeof(expr)
其必须要知道类型的expr
同时,否则怎么回事能告诉我们的大小,不知道类型?难道真的有可能知道大小,而不知道表达式的类型?
如果它不知道类型,那么编译器告诉我们什么(如果不是类型)的大小?我的意思是,对编译器(以及人类)也没有意义!sizeof(unknowntype)
Joh*_*itb 11
它只使用编译器魔法.就像,海湾合作委员会的__typeof__
.对于不提供这种魔术的编译器,它提供了一个可以检测某些表达式类型的仿真,但是在完全未知类型的情况下失败.
可能的实现可以是具有接受给定类型的表达式的函数列表,然后使用类模板从该类型分派到数字.为了使函数模板将数字作为编译时实体返回,我们将其放入数组维度
template<typename> struct type2num;
template<int> struct num2type;
template<typename T> typename type2num<T>::dim &dispatch(T const&);
Run Code Online (Sandbox Code Playgroud)
然后它从该数字返回到类型,以便我们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; }
Run Code Online (Sandbox Code Playgroud)
有了这个,你可以写
#define EMUL_TYPEOF(E) \
num2type<sizeof dispatch(E)>::type
Run Code Online (Sandbox Code Playgroud)
无论什么时候需要注册一个类型,你都要写
REGISTER_TYPE(int, 1);
REGISTER_TYPE(unsigned int, 2);
// ...
Run Code Online (Sandbox Code Playgroud)
当然,现在你发现你需要一种机制来接受vector<T>
,你T
事先不知道然后它变得任意复杂.您可以创建一个系统,其中数字不仅仅意味着类型.这可能有效:
#define EMUL_TYPEOF(E) \
build_type<sizeof dispatch_1(E), sizeof dispatch_2(E)>::type
Run Code Online (Sandbox Code Playgroud)
这可以通过某种系统的映射来检测类似的类型int
,也可以检测类型shared_ptr<int>
- 换句话说,不是类模板特化的类型,以及具有一个模板参数的类模板特化
所以这就成了
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;
};
Run Code Online (Sandbox Code Playgroud)
我们还需要更改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; \
}; \
}
Run Code Online (Sandbox Code Playgroud)
注册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);
Run Code Online (Sandbox Code Playgroud)
您可能还希望为每种类型注册多个数字,每个const/volatile组合一个数字,或者每种类型可能需要多个数字用于记录*
,&
等等.您也希望支持vector< vector<int> >
,因此模板参数也需要多个数字,以build_type
递归方式调用自身.由于您可以创建任意长整数列表,因此无论如何都可以将任何内容编码到该序列中,因此您可以根据自己的创造力来表示这些内容.
最后,你可能重新实现BOOST_TYPEOF :)
从内存来看,boost :: typeof是通过一些?:hacks实现的.首先,你从一个可以转换为任何其他类的类开始,比如
class something {
public:
template<typename T> operator const T&() {
return *(T*)0;
}
};
Run Code Online (Sandbox Code Playgroud)
?:规则规定如果双方具有相同的类型,则结果为该类型.否则,如果一种类型可以转换为另一种类型,那就是结果类型.所以通过做
true ? something() : expr;
Run Code Online (Sandbox Code Playgroud)
结果类型是(一个const引用)expr的类型 - 但expr实际上从未被计算过,因为它在假分支上.那么你将它传递给已经有参数推导的地方,比如函数参数.
template<typename T> void x(const T& t) {
// T is the type of expr.
}
Run Code Online (Sandbox Code Playgroud)
这有点复杂,因为从内存来看,C++ 03没有引用崩溃,所以它可能比这个例子更复杂 - 最有可能使用SFINAE和类型特征.
如何将其转换为可以传递到模板的实际编译时类型,我不知道.对于提供decltype()的C++ 03,C++ 03语言存在更大的问题,如ODR和声明/定义顺序,没有移动语义等,这比不提供decltype要糟糕得多.