是否可以在编译时检查类型是否派生自模板的某个实例化
Is it possible to check at compile time whether a type is derived from some instantiation of a template?
我想编写一个模板函数,如果传递的类型派生自另一个类的任何模板实例化,则该函数的行为方式为一种方式,如果不是,则以另一种方式运行。
我认为下面的代码捕捉了我想做的事情。 不幸的是,呼叫者为double
和Derived
打印"通用"。
#include <iostream>
template <typename T>
struct Base
{
};
struct Derived
:
public Base<int>
{
};
template <typename T>
void Foo(const T&)
{
std::cout << "generic" << std::endl;
}
template <typename T>
void Foo(const Base<T>&)
{
std::cout << "derives from Base<T>" << std::endl;
}
template <typename T>
void Caller(const T& t)
{
Foo(t);
}
int main()
{
double x;
Caller(x);
Derived d;
Caller(d);
return 0;
}
(请注意,Caller 不知道其参数可能派生自Base
的哪个实例化。
它调用const T&
重载,因为它比const base<T>&
匹配更好。原因是调用第一个不需要转换,而第二个需要派生到基数的转换。
这里有一个快速的技巧,向您展示如何完成它(请注意引入的基类(:
#include <iostream>
#include <type_traits>
struct EvenMoreBase {};
template <typename T>
struct Base : EvenMoreBase
{
};
struct Derived
:
public Base<int>
{
};
template <typename T>
typename std::enable_if<!std::is_base_of<EvenMoreBase, T>::value>::type
Foo(const T&)
{
std::cout << "generic" << std::endl;
}
template <typename T>
void Foo(const Base<T>&)
{
std::cout << "derives from Base<T>" << std::endl;
}
template <typename T>
void Caller(const T& t)
{
Foo(t);
}
int main()
{
double x;
Caller(x);
Derived d;
Caller(d);
return 0;
}
如果您能够使用 C++11(或一般<type_traits>
(,以下也是一个可能的解决方案,不仅涵盖 T : Base<T>
类型,即 CRTP 的实例,还涵盖没有其他基类的T : Base<U>
,如您的示例中要求的那样。
#include <iostream>
#include <type_traits>
template <typename T>
struct Base
{
typedef T base_value_type;
};
struct Derived : public Base<int>
{
};
template <typename T, typename = T>
struct IsDerived
{
static const bool value = false;
};
template <typename T>
struct IsDerived<T, typename std::enable_if<std::is_base_of<Base<typename T::base_value_type>, T>::value, T>::type>
{
static const bool value = true;
};
template <typename T>
void Caller(const T&)
{
std::cout << IsDerived<T>::value << std::endl;
}
int main()
{
Caller(double()); // false
Caller(Derived()); // true
return 0;
}
请注意typedef T base_value_type
- 可以随便叫。这个想法是,从Base<U>
派生的每个类型T
都可以利用基础模板参数的知识。T == U
与否并不重要。一旦传入没有typedef T base_value_type
的T
,尝试替换第二个参数将失败,因此不会生成此特定T
的专用化。
编辑:处理完您的评论后,并受到我发布的线程的启发,我试图在检查某些时间类型T : Base<U>
时以某种方式提取一些基本参数U
。我认为这不能以您想要的方式完成,即您传递任何T
并提取U
.但是,您可以做两件事。
简单的解决方案:如果您可以控制派生类的实现方式,则无需在基类中添加typedef
,只需在派生类中添加相应的 typedef:
template <typename BaseParamType>
class Derived : public Base<BaseParamType>
{
public:
typedef BaseParamType base_param_type;
}
或者,如果您不希望派生类也是类模板,只需将类型直接硬编码到类型中(您已经知道 base 参数的类型(:
class Derived : public Base<int>
{
public:
typedef int base_param_type;
}
更复杂的解决方案:至少对于可能的U
的预期子集,您可以执行以下操作:
template <typename DerivedType,
typename BaseParamType = DerivedType,
bool = std::is_base_of<Base<BaseParamType>, DerivedType>::value>
struct Extract
{
typedef BaseParamType type;
};
template <typename T, typename U>
struct Extract<T, U, false>;
int main()
{
Extract<DerivedCRTP>::type; // CRTP - trivial
Extract<Derived, int>::type; // type == int, Derived is derived from Base<int>
Extract<Derived, double>::type; // compile-time error, undefined template
return 0;
}
这并不像将某个类型的实例传递给推导模板函数并神奇地拥有它那么方便,但您至少可以测试某些类型T
是否派生自Base<U>
,如果不是,则会出现编译时错误。
由于基类必须是具体的类(而不是模板(,因此无法知道它是模板类还是非模板类。
换句话说:
struct A1 : public B1
{};
struct A2 : public B2<int>
{};
在这两种情况下,两个基类都是具体类型。
- 检查某些类型是否是模板类 std::optional 的实例化
- 检查类是否在方法中实例化
- 实例化模板时,我是否必须显式显示参数包中的类型?
- 是否可以将指向未实例化的对象的指针用作C++中的变量?
- 初始值设定项列表是否足够静态以允许实例化模板?
- 如果专用化已经隐式实例化,它是否隐式实例化?
- 从模板基类派生是否在派生类声明的点实例化模板
- 在编译时检查未实例化的类模板是否继承自其第一个模板参数
- 非类型引用参数可以在运行时修改,这是否意味着模板可以在运行时实例化?
- 是否实例化了所有有效的模板
- 检查类型是否为模板的实例化
- C++标准是否明确允许/禁止实例化不完整类型的 std::函数?
- 是否可以实例化具有已删除构造函数和析构函数的非聚合类?
- 编译时检查是否有两个具有相同模板参数的模板实例化
- 在另一个对象 B 中创建对象 A 时,对象 A 是否是对象 B 的本地对象,对象 A 是否会存在于对象 B 的实例化之外?
- 是否未指定在未评估的上下文中实例化模板/lambda
- JNI-是否可以用其他类替换类的实例化
- 使用显式命名空间限定符时模板实例化行为是否发生变化?
- 模板类静态在最终二进制文件中跨共享库以不同方式实例化是否一致
- std::map<K,V>::iterator 是否实例化 std::map<K,V>?