【发布时间】:2015-03-17 14:24:00
【问题描述】:
我一直在研究一种生成有关在 C++ 中包装其他类的类的编译时信息的方法。在我要问的问题的一个最小示例中,这样的包装类:
- 包含一个
typedef WrappedType,定义了包装类的类型;和 - 重载了一个名为
IsWrapper的结构模板,以表明它是一个包装类。
有一个名为WrapperTraits 的结构模板,然后可用于确定包装类型层次结构的(非包装)根类型。例如。如果包装类是名为Wrapper<T> 的类模板,则Wrapper<Wrapper<int>> 的根类型将是int。
在下面的代码 sn-p 中,我实现了一个名为GetRootType<T> 的递归结构模板,它定义了一个typedef RootType,它给出了包装器类型T 的根类型。 WrapperTraits 的给定定义只包含GetRootType 定义的根类型,但实际上它会有一些额外的成员。为了测试它,我编写了一个普通函数f,它接受int,以及一个重载函数模板f,它接受一个以int 作为根类型的任意包装类。我使用 SFINAE 来区分它们,通过在函数模板的返回类型中使用std::enable_if 来检查f 的参数的根类型是否为int(如果f 的参数不是包装器,试图确定其根类型将失败)。在我问我的问题之前,这里是代码 sn-p:
#include <iostream>
#include <type_traits>
// Wrapper #######################################
template<class T>
struct Wrapper {typedef T WrappedType;};
template<class T, class Enable=void>
struct IsWrapper: std::false_type {};
template<class T>
struct IsWrapper<Wrapper<T> >: std::true_type {};
// WrapperTraits #######################################
template<
class T,
bool HasWrapper=
IsWrapper<T>::value && IsWrapper<typename T::WrappedType>::value>
struct GetRootType {
static_assert(IsWrapper<T>::value,"T is not a wrapper type");
typedef typename T::WrappedType RootType;
};
template<class T>
struct GetRootType<T,true> {
typedef typename GetRootType<typename T::WrappedType>::RootType RootType;
};
template<class T>
struct WrapperTraits {
typedef typename GetRootType<T>::RootType RootType;
};
// Test function #######################################
void f(int) {
std::cout<<"int"<<std::endl;
}
// #define ROOT_TYPE_ACCESSOR WrapperTraits // <-- Causes compilation error.
#define ROOT_TYPE_ACCESSOR GetRootType // <-- Compiles without error.
template<class T>
auto f(T) ->
typename std::enable_if<
std::is_same<int,typename ROOT_TYPE_ACCESSOR<T>::RootType>::value
>::type
{
typedef typename ROOT_TYPE_ACCESSOR<T>::RootType RootType;
std::cout<<"Wrapper<...<int>...>"<<std::endl;
f(RootType());
}
int main() {
f(Wrapper<int>());
return 0;
}
这会正确编译 (try it here) 并产生输出:
Wrapper<...<int>...>
int
但是,我使用GetRootType 来确定对std::enable_if 的调用中的根类型。如果我改为使用WrapperTraits 来确定根类型(您可以通过更改ROOT_TYPE_ACCESSOR 的定义来做到这一点),GCC 会产生以下错误:
test.cpp: In instantiation of ‘struct WrapperTraits<int>’:
test.cpp:49:6: required by substitution of ‘template<class T> typename std::enable_if<std::is_same<int, typename WrapperTraits<T>::RootType>::value>::type f(T) [with T = int]’
test.cpp:57:15: required from ‘typename std::enable_if<std::is_same<int, typename WrapperTraits<T>::RootType>::value>::type f(T) [with T = Wrapper<int>; typename std::enable_if<std::is_same<int, typename WrapperTraits<T>::RootType>::value>::type = void]’
test.cpp:62:19: required from here
test.cpp:21:39: error: ‘int’ is not a class, struct, or union type
bool HasWrapper=IsWrapper<T>::value && IsWrapper<typename T::WrappedType>::value>
我的问题是:C++ 标准中关于参数推导的规则解释了为什么使用WrapperTraits 会导致编译错误,但使用GetRootType 不会? 请注意我要问的内容这是为了能够理解为什么会出现这个编译错误。我对可以进行哪些更改以使其正常工作不太感兴趣,因为我已经知道将 WrapperTraits 的定义更改为此可以修复错误:
template<
class T,
class Enable=typename std::enable_if<IsWrapper<T>::value>::type>
struct WrapperTraits {
typedef typename GetRootType<T>::RootType RootType;
};
template<class T>
struct WrapperTraits<T,typename std::enable_if<!IsWrapper<T>::value>::type> {
};
但是,如果有人能看到更优雅的写作方式f 和WrapperTraits,我会非常有兴趣看到它!
【问题讨论】:
标签: c++ templates sfinae typetraits enable-if