草庐IT

c++ - 在编译时通过模板自动判断类型是否为抽象基类

coder 2024-02-18 原文

是否可以在编译时自动判断一个类是否为抽象基类?

我有一个对象工厂,通过其他通用代码,它有时会用抽象基类类型实例化。该代码无法编译,因为它在 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
Attempting to create a Foo class.
Foo created
Attempting to create a Baz class.
Cannot create and Abstract Base Class!
Create failed on type_info::name() = 3Baz

edit 1 @jwismar pointed me towards Boost's is_abstract implementation. Honestly, looking at the code and trying to deduce what boost is doing is very painful. Can someone boil down what trick they're using? (edit 2 actually, I was looking at the wrong bit of code, and I figured it down below in edit 2)

@raj Yes, there is a constraint that the class must have a default public constructor. Its not entirely generic, but it provides functionality for 99% of the types I care about. Adding a create() method is not an option because I don't control some of the classes being wrapped (3rd party code).

@DennisZickefoose The code does compile -- with template specializations to handle ABCs. Yes, the design could be improved to ensure the code that instantiates the create() method with an ABC doesn't do so, but that code is also performing other duties which make sense on ABCs and non-ABCs alike. At this point it would be a major re-write and I'm looking for a more short term solution.

Both @raj and @DennisZickefoose make good points about the design of the example and the underlying code base, but I'm really only interested in the topic's question of how to determine ABC-ness of a type at compile time. Preferably without Boost. My rationale for such a need is orthogonal to the question at hand.

edit 2 Since I can't answer my own question with out 100 reputation, I'll post my answer here:

I was able to understand the Boost is_abstract code enough to create a version of isAbstractBaseClass that works for my needs. It uses SFINAE to fallback to the check_sig(...) version in the case of an ABC type.

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

最佳答案

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

关于c++ - 在编译时通过模板自动判断类型是否为抽象基类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6157402/

有关c++ - 在编译时通过模板自动判断类型是否为抽象基类的更多相关文章

  1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  4. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  5. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  6. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  7. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

    我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

  8. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  9. ruby - 检查日期是否在过去 7 天内 - 2

    我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/

  10. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

随机推荐