请考虑以下代码片段:
#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*
(因为基类不知道它的派生类做了什么)。
总结(有奖金):
如果您的实现使用模板但不考虑作用域,您将无法在非本地作用域中访问该类型,因为您必须将它存储在一个容器中,该容器知道一些它不知道的事情。
如果您的实现使用模板但考虑了作用域(如上所示),您将无法访问原始类型,因为您必须通过知道一些它不知道的事情的基类访问它。
奖励:如果您的实现使用
std::type_info
,std::type_index
(C++11) 或std::any::type
(C++17) 您将能够访问“类型”,但您访问的类型不能用于类型转换。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/