【发布时间】:2011-05-27 20:32:12
【问题描述】:
是否可以在编译时自动判断一个类是否为抽象基类?
我有一个对象工厂,通过其他通用代码,有时会使用抽象基类类型进行实例化。该代码无法编译,因为它在 ABC 上调用了 new T()。在这种情况下,我最终不得不专门为每个 ABC 的对象工厂创建代码来代替 assert(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;
}
输出:
> c++ abstract.cpp && ./a.out 试图创建一个 Foo 类。 Foo 创建 试图创建一个 Baz 类。 无法创建和抽象基类! 在 type_info::name() = 3Baz 上创建失败edit 1 @jwismar 向我指出了 Boost 的 is_abstract 实现。老实说,查看代码并试图推断 boost 在做什么是非常痛苦的。有人可以总结出他们使用的是什么技巧吗? (edit 2 实际上,我查看了错误的代码,我在下面的编辑 2 中发现了它)
@raj 是的,有一个约束是类必须有一个默认的公共构造函数。它并不完全通用,但它为我关心的 99% 的类型提供了功能。添加 create() 方法不是一个选项,因为我不控制一些被包装的类(第 3 方代码)。
@DennisZickefoose 代码确实可以编译——使用模板特化来处理 ABC。是的,可以改进设计以确保使用 ABC 实例化 create() 方法的代码不会这样做,但该代码还执行其他对 ABC 和非 ABC 有意义的职责。在这一点上,这将是一次重大的重写,我正在寻找一个更短期的解决方案。
@raj 和@DennisZickefoose 都对示例的设计和底层代码库提出了很好的观点,但我真的只对该主题的问题感兴趣,即如何在编译时确定类型的 ABC 性。最好没有Boost。我提出这种需求的理由与手头的问题是正交的。
编辑 2 由于没有 100 名声望,我无法回答我自己的问题,所以我将在此处发布我的答案:
我能够理解 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 };
};
【问题讨论】:
-
你已经对你的类有一个约束,它必须有一个不带任何参数的构造函数。所以一开始不是很通用。如果您强制执行该约束,您还不如让该类有一个名为 Foo* create() 的方法并将责任委托给各个类而不是您的对象工厂
-
您还应该厌倦由于其他原因而没有可访问的默认构造函数的类。
-
我也不确定为什么如果没有您的帮助,这还不是编译时错误。请求抽象类型的具体实例的通用代码才是真正的罪魁祸首。
-
哦等等,你想要它编译。太傻了,你的代码有一个错误,如果失败,你需要修复。
-
您可以回答自己的问题,但可能有时间限制。