请考虑以下代码片段:
#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;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码编译并运行良好,正如您可以在 Godbolt 在线看到的那样。但代码看起来相当笨拙。c++17 或 c++20 中是否有更好的撤消类型擦除的解决方案?
除了优雅之外,正如评论中提到的,您的代码片段可以简化如下:
#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;
}
Run Code Online (Sandbox Code Playgroud)
#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;
}
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,您的SaveType类都没有被使用,因为它只在本地范围内工作(不指定类型),因此是多余的。要纠正这个问题,您必须实现@MichaelAaronSafyan 的代码片段:
#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;
}
Run Code Online (Sandbox Code Playgroud)
这允许您提前存储SaveType在容器中,从而在多个作用域中使用它,但是(如上所示),它有自己的问题,因为它返回void*而不是T*(因为基类不知道其派生类是什么)做)。
总结一下(还有奖金):
如果您的实现使用模板但不考虑范围,您将无法访问非本地范围中的类型,因为您必须将其存储在知道它不知道的东西的容器中。
如果您的实现使用模板但考虑了范围(如上所示),您将无法访问原始类型,因为您必须通过知道它不知道的东西的基类来访问它。
std::type_info奖励:如果您的实现使用std::type_index(C++11) 或std::any::type(C++17),您将能够访问“类型”,但您访问的类型不能用于类型转换。
超级奖励:如果您的实现使用 aCovariant Return Type您仍然无法访问“类型”,因为它的隐式重新转换是肤浅的。
对于实现#1,您只能在删除类型的同一上下文中撤消类型删除。
对于实现#2(如果适用),您可以放弃直接访问,使基类不需要知道它不知道的内容,从而允许派生类对只有它知道的信息进行操作。这就是所谓的"Tell, Don't Ask"原则。
对于实现 #3, all typeid、decltype(C++11) 和std::any::type(C++17) 可以帮助您加快引用可能类型池的过程(除非您确实知道该池由少数类型组成)对于特定类型,我不建议手动编写代码,而是根据同样生成的可能类型列表以编程方式生成它)。
对于实现 #4,只需将其视为死胡同。
| 归档时间: |
|
| 查看次数: |
1246 次 |
| 最近记录: |