是否可以在编译时自动判断一个类是否是抽象基类?
我有一个对象工厂,通过其他通用代码,有时会使用抽象基类类型进行实例化。该代码无法编译,因为它在 ABC 上调用了 new T()。在这种情况下,我最终必须专门为每个 ABC 创建对象工厂代码来代替断言(0)。如果可以在编译时自动确定类型是否是 ABC,那么这种专业化就可以自动化。
一个简化的示例如下:
// this program code compiles w/ gcc 4.4
#include <iostream>
#include <typeinfo>
// How to automatically specialize this class at compile-time?
template<typename T>
struct isAbstractBaseClass
{
enum { VALUE = 0 };
};
// Factory to create T, lives in a struct to allow default template parameters
template<typename T, int ABSTRACT = isAbstractBaseClass<T>::VALUE >
struct Create
{
static T* create()
{
return new T();
}
};
// specialize Create for abstract base classes
template<typename T>
struct Create<T, 1>
{
static T* create()
{
std::cout << "Cannot create and Abstract Base Class!\n";
std::cout << "Create failed on type_info::name() = " << typeid(T).name() << "\n";
return 0;
}
};
struct Foo
{
Foo() { std::cout << "Foo created\n"; }
};
struct Baz
{
virtual void bar() = 0; // make this an Abstract Base Class
};
// template specialize on Baz to mark it as an Abstract Base Class
// My Question: is it possible to automatically determine this at compile-time?
template<> class isAbstractBaseClass<Baz> { enum { VALUE = 1 }; };
int main()
{
std::cout << "Attempting to create a Foo class.\n";
delete Create<Foo>::create();
std::cout << "Attempting to create a Baz class.\n";
delete Create<Baz>::create();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
> C++ Abstract.cpp && ./a.out 尝试创建 Foo 类。 Foo 创建 尝试创建 Baz 类。 无法创建抽象基类! 在 type_info::name() = 3Baz 上创建失败
编辑 1 @jwismar 向我指出了 Boost 的 is_abstract 实现。老实说,查看代码并试图推断 boost 正在做什么是非常痛苦的。有人可以总结一下他们使用的是什么技巧吗?(实际上编辑2,我正在查看错误的代码,我在下面的编辑2中找到了它)
@raj是的,有一个限制,即该类必须有一个默认的公共构造函数。它并不完全通用,但它提供了我关心的 99% 类型的功能。添加 create() 方法不是一个选项,因为我无法控制某些被包装的类(第三方代码)。
@DennisZickefoose 代码确实可以编译——使用模板专门化来处理 ABC。是的,可以改进设计以确保使用 ABC 实例化 create() 方法的代码不会这样做,但该代码还执行其他对 ABC 和非 ABC 都有意义的职责。此时,这将是一次重大重写,我正在寻找更短期的解决方案。
@raj 和 @DennisZickefoose 都对示例的设计和底层代码库提出了很好的观点,但我实际上只对主题的问题感兴趣,即如何在编译时确定类型的 ABC 性。最好没有Boost。我对这种需要的理由与当前的问题是正交的。
编辑2 Since I can't answer my own question with out 100 reputation, I'll post my answer here:
我能够充分理解 Boost is_abstract 代码,以创建适合我的需求的 isAbstractBaseClass 版本。在 ABC 类型的情况下,它使用SFINAE回退到 check_sig(...) 版本。
template<class T>
struct isAbstractBaseClass
{
// Inspired by boost/type_traits/is_abstract.hpp
// Deduction fails if T is void, function type,
// reference type (14.8.2/2)or an abstract class type
// according to review status issue #337
template<class U>
static char check_sig(U (*)[1]);
template<class U>
static short check_sig(...);
//
enum { VALUE = sizeof(isAbstractBaseClass<T>::template check_sig<T>(0)) - 1 };
};
Run Code Online (Sandbox Code Playgroud)