【问题标题】:Automatically determine if type is Abstract Base Class via templates at compile-time在编译时通过模板自动确定类型是否为抽象基类
【发布时间】: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() 的方法并将责任委托给各个类而不是您的对象工厂
  • 您还应该厌倦由于其他原因而没有可访问的默认构造函数的类。
  • 我也不确定为什么如果没有您的帮助,这还不是编译时错误。请求抽象类型的具体实例的通用代码才是真正的罪魁祸首。
  • 哦等等,你想要它编译。太傻了,你的代码有一个错误,如果失败,你需要修复。
  • 可以回答自己的问题,但可能有时间限制。

标签: c++ templates


【解决方案1】:

Boost Type Traits 库有一个is_abstract 函子。您可以直接使用它,也可以查看实现,看看他们是如何处理的。

【讨论】:

  • 嘿,数字。 Boost 似乎是这类问题的首选答案。谢谢指点。
  • @McKay 确实如此。那些家伙摇滚。
【解决方案2】:

或者只是删除这个:

// 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;
  }
};

那么当您尝试创建 abc 时,编译器只会发出错误。

【讨论】:

  • 贾斯汀,你完全没有理解我的意思。它需要编译和执行。在实际代码库中,有通用模板代码为所有类型(包括 ABC)实例化创建函数。即使代码从未被调用,它也需要是可编译的(如果调用了 ABC 的创建者,则专门化 assert(0))。
  • @McKay:除了不可能创建抽象类型。这是他们定义的一部分。如果程序要求创建该函数,则您有一个错误并且它不应该编译。将检查移动到运行时间是荒谬的。让类型系统为您工作,这就是它的用途。
  • 我同意丹尼斯的观点。我发现重定向实例化流程以支持运行时assertion 很奇怪,其中编译错误是理想的(imo)。我明白你在问什么,而不是为什么。 OP 没有提供避免有利错误机制的理由 - 这就是我发布答案的原因。
猜你喜欢
  • 1970-01-01
  • 2021-11-22
  • 1970-01-01
  • 2012-03-13
  • 2010-12-18
  • 2011-06-12
  • 2011-08-25
  • 1970-01-01
相关资源
最近更新 更多