在运行时检查继承是否只有其中一种类型和 void*

Kaa*_*Kaa 4 c++

我需要一种方法来在运行时检查类型是否通过继承与匿名对象的类型(出于我的目的的 void 指针)相关。

假设我有两种类型:

class Base
{
public:
  virtual ~Base();
};

class Derived : public Base
{
public:
  ~Derived();
};

Base *base = new Derived();
Derived *derived = new Derived();
Run Code Online (Sandbox Code Playgroud)

我需要实现以下功能:

template <typename T>
T *isRelated(void *obj, void *tag)
{
  //If T is related to obj's real type (established based on tag value)
  //    return reinterpret_cast<T*>(obj);
  //else
  //    return nullptr;
}
Run Code Online (Sandbox Code Playgroud)

目前,我可以通过在 typetag 中存储任何一个值(在编译时确定并作为值返回)并比较是否相等来检查 obj 是否与 T 类型相同

换句话说,当前的 typetag 实现类似于:

template <typename T>
void *getTypeTag();

template <>
void *getTypeTag<Base>()
{
  return 1;
}

template <>
void *getTypeTag<Derived>()
{
  return 2;
}
Run Code Online (Sandbox Code Playgroud)

我可以在 typetag 中存储什么来检查继承关系?

我正在寻找一个可扩展的解决方案,因为会有许多类具有频繁的继承关系。

编辑/澄清:

  • 我正在使用第 3 方 API,该 API 只会给我 void 指针,因此不可能基于类型化 obj 重载 isRelated
  • 动态转换是不可能的,因为它们在 T 和 void* 之间不起作用

Kaa*_*Kaa 8

我添加了另一个答案,它不依赖于未定义的行为。


警告:下面的答案取决于未定义的行为

C++14 标准: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf

第 10 章第 5 节:

基类子对象在最派生对象 (1.8) 中分配的顺序未指定

按照这个逻辑,reinterpret_cast我的回答将依赖未定义的行为来工作。此外,考虑到void*正在转换 - 在不知道原始类型的情况下,似乎无法将其安全地转换为基类。


提出了一种可能的解决方案,效果很好 - 但它在以下方面受到限制:

  • 我必须使用反映原始类型继承的继承模式来专门化 TypeTagSpec
  • 验证发生在类型层,而不是对象指针层(这在最后一段中已阐明)

考虑到设计的示例类 Base 和 Derived,我们假设我们将再添加一个类“Unrelated”:

// Some class which is not related to our inheritance check, for testing later
class Unrelated
{
public:
    virtual ~Unrelated(){}
};
Run Code Online (Sandbox Code Playgroud)

现在,后续专门的 TypeTagSpec 结构将被实例化并作为 void *tag 传递给某些 API:

// This is the base class, and we will initially cast all void *tag objects to this type
struct TypeTag
{
  virtual ~TypeTag(){}
};

// Unspecialized template derives from TypeTag, to give us a way 'up' the inheritance
template <typename T>
struct TypeTagSpec : TypeTag
{
  virtual ~TypeTagSpec(){}
};

// Specialized testing typetag for Unrelated type, used for testing
template <>
struct TypeTagSpec<Unrelated> : TypeTag
{
  virtual ~TypeTagSpec(){}
};

// Specialized Base typetag, nothing special
template <>
struct TypeTagSpec<Base> : TypeTag
{
  virtual ~TypeTagSpec(){}
};

// Magic here - specialized tagtype for Derived actually inherits from Base typetag, giving a clear inheritance line
template <>
struct TypeTagSpec<Derived> : TypeTagSpec<Base>
{
  virtual ~TypeTagSpec(){}
};
Run Code Online (Sandbox Code Playgroud)

这给我们留下了代码:

// The solution
template <typename T>
T *isRelated(void *obj, void *tag)
{
  const TypeTag *typetag = reinterpret_cast<TypeTag*>(tag);
  if(dynamic_cast<const TypeTagSpec<T>*>(typetag))
    return reinterpret_cast<T*>(obj);
  return nullptr;
}

TEST(TypeTag, Inheritance)
{
  Derived *derived = new Derived();
  TypeTag *typetag = new TypeTagSpec<Derived>();

  // Test begins here
  void *obj = derived;
  void *tag = typetag;

  EXPECT_EQ(isRelated<Base>(obj, tag), derived);
  EXPECT_EQ(isRelated<Derived>(obj, tag), derived);
  EXPECT_EQ(isRelated<Unrelated>(obj, tag), nullptr);
  // Test ends here

  delete derived;
  delete typetag;
}
Run Code Online (Sandbox Code Playgroud)

需要注意的是,问题是要查找类型是否相关,因此这个测试将通过:

 auto *base = new Base();
 auto *tag = new TypeTagSpec<Base>();
 EXPECT_EQ(isRelated<Derived>(base, tag), base);
Run Code Online (Sandbox Code Playgroud)

在这个测试中,isRelated仍然会返回一个指向base的指针;实际上,这个指针可能并不总是有效/可用,取决于正在转换的类。由于我的用例涉及向上转换类型层关系检查,因此我不关心这个对象有效性的细微差别 - 并且从类型角度(而不是对象角度)来看,该关系在技术上仍然有效。

旁注:无论谁在提出问题后 5 分钟内对我原来的问题投了反对票,都是小气的,投反对票比提供帮助更快乐。