c++ - 是否有更优雅的方法来使用 c++17/c++20 撤消 "saved"类型的类型删除?

标签 c++ type-erasure

请考虑以下代码片段:

#include <iostream>

template <typename T>
class SaveType {
 public:
  T* allocate() const { return new T; }
  T* cast(void* obj) const { return static_cast<T*>(obj); }
};

int main() {
  int i = 4;
  // "save" the type of the object i in SType:
  SaveType<decltype(i)> SType;

  // do type erasure
  void* z = static_cast<void*>(&i);
  // do stuff with z ...

  // undo type erasure only with the help of SType
  decltype(SType.allocate()) h = SType.cast(z);
  std::cout << *h << std::endl;
}

上面的代码可以正常编译和运行see online at Godbolt .但是代码看起来相当笨拙。在 c++17 或 c++20 中是否有更好的撤消类型删除解决方案?

最佳答案

撇开优雅不谈,如评论中所述,您的代码片段可以简化如下:

C++11

#include <iostream>

int main() {
  int i = 4;
  // "save" the type of the object i in SType:
  using SType = decltype(i); // or with the older syntax: typedef decltype(i) SType;

  // do type erasure
  void* z = static_cast<void*>(&i);
  // do stuff with z ...

  // undo type erasure only with the help of SType
  auto h = static_cast<SType*>(z); // or with the older less safe C-style syntax: auto h = (SType*)z;
  std::cout << *h << std::endl;
}

C++17

#include <any>
#include <iostream>

int main() {
  int i = 4;
  // "save" the type of the object i in SType:
  using SType = decltype(i);

  // do type erasure
  std::any z = i;
  // do stuff with z ...

  // undo type erasure only with the help of SType
  auto h = std::any_cast<SType>(z);
  std::cout << h << std::endl;
}

在这两种情况下,您的 SaveType 类都没有被使用,因为它只在本地范围内工作(没有指定类型),因此是多余的。要纠正此问题,您必须实现@MichaelAaronSafyan 的 code snippet :

C++14

#include <iostream>
#include <memory>

class SaveType
{
    public:
        virtual ~SaveType(){}
        virtual void* allocate()const=0;
        virtual void* cast(void* obj)const=0;
};

template<typename T> class Type : public SaveType
{
      public:
         virtual void* allocate()const{ return new T; }
         virtual void* cast(void* obj)const{ return static_cast<T*>(obj); }
};

int main() {
  int i = 4;
  // "save" the type of the object i in SType:
  std::unique_ptr<SaveType> SType = std::make_unique<Type<int>>();;

  // do type erasure
  void* z = static_cast<void*>(&i);
  // do stuff with z ...

  // undo type erasure only with the help of SType
  decltype(SType->allocate()) h = SType->cast(z);
  std::cout << typeid(h).name() << std::endl;

  // undo type erasure manually
  auto h2 = *(int*) z;
  std::cout << h2 << std::endl;
}

这允许您提前将 SaveType 存储在容器中,因此可以在多个范围内使用它,但是(如上所示),它有自己的问题,因为它返回 void* 而不是 T*(因为基类不知道它的派生类做了什么)。


总结(有奖金):

  1. 如果您的实现使用模板但不考虑作用域,您将无法在非本地作用域中访问该类型,因为您必须将它存储在一个容器中,该容器知道一些它不知道的事情。

  2. 如果您的实现使用模板但考虑了作用域(如上所示),您将无法访问原始类型,因为您必须通过知道一些它不知道的事情的基类访问它。

  3. 奖励:如果您的实现使用 std::type_info , std::type_index (C++11) 或 std::any::type (C++17) 您将能够访问“类型”,但您访问的类型不能用于类型转换。

  4. super 奖励:如果您的实现使用 Covariant Return Type您仍然无法访问“类型”,因为 its implicit reconversion is superficial .


对于实现 #1,您只能在删除它的同一上下文中撤消类型删除。

对于实现 #2,(如果适用)您可以放弃直接访问并使其成为基类不需要知道它不知道的内容,从而允许派生类仅根据它知道的信息进行操作。这叫做 "Tell, Don't Ask"原则。

对于实现 #3,所有 typeid , decltype (C++11) 和 std::any::type (C++17) 可以帮助您加快引用可能类型池的过程(除非您确实知道该池由少数特定类型组成,否则我不建议手动编写代码,而是基于同样生成的可能类型列表以编程方式生成它。

对于实现 #4,只需将其视为死胡同即可。

关于c++ - 是否有更优雅的方法来使用 c++17/c++20 撤消 "saved"类型的类型删除?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61385288/

相关文章:

c++ - 如何在 MFC 中向 CMenu 添加子菜单?

c++ set ordered iterating - 迭代集的结果是否按标准排序?

c++ - 我需要打印静态矩阵重载 "<<"运算符

c++ - C++ 中的类型删除是什么?

java - 实现不兼容的接口(interface)

Java 泛型和 Groovy 重载

java - java 中类型删除如何工作?

c# - 从 C# 调用 DLL 时出现内存 AccessViolationException 错误

c++ - 按类型在一组数据成员中进行选择,其中每种类型仅使用一次

java - 为什么父类(super class)字段的泛型类型不会被删除到子类型中的具体绑定(bind)?