(通过类型擦除,我指的是隐藏与类相关的一些或全部类型信息,有点像Boost.Any。)
我想了解类型擦除技术,同时也想分享我所了解的技术。我希望能找到一些疯狂的技巧,这些技巧可能是某个人在最困难的时刻想出来的。:)
我所知道的第一种最明显、最常用的方法是使用虚函数。只需将类的实现隐藏在基于接口的类层次结构中。许多Boost库都使用这种方法,例如Boost.Any用于隐藏类型,Boost.Shared_ptr用于隐藏(释放)分配机制。
然后还有使用函数指针指向模板函数的选项,同时将实际对象保存在一个void*
指针中,就像Boost.Function所做的那样,隐藏了函数对象的真实类型。示例实现可以在问题的末尾找到。所以,针对我的实际问题:
你还知道其他的类型擦除技术吗?如果可能的话,请提供它们的示例代码、使用案例、你对它们的经验,以及进一步阅读的链接。
编辑 (由于我不确定是将此作为答案添加,还是只编辑问题,所以我选择更安全的方式。) 另一种很好的技巧是在不使用虚函数或void*操作的情况下隐藏某物的实际类型,这是GMan在这里使用的方法here,与my question有关,关于这个方法的工作原理。
示例代码:
#include <iostream>
#include <string>
// NOTE: The class name indicates the underlying type erasure technique
// this behaves like the Boost.Any type w.r.t. implementation details
class Any_Virtual{
struct holder_base{
virtual ~holder_base(){}
virtual holder_base* clone() const = 0;
};
template<class T>
struct holder : holder_base{
holder()
: held_()
{}
holder(T const& t)
: held_(t)
{}
virtual ~holder(){
}
virtual holder_base* clone() const {
return new holder<T>(*this);
}
T held_;
};
public:
Any_Virtual()
: storage_(0)
{}
Any_Virtual(Any_Virtual const& other)
: storage_(other.storage_->clone())
{}
template<class T>
Any_Virtual(T const& t)
: storage_(new holder<T>(t))
{}
~Any_Virtual(){
Clear();
}
Any_Virtual& operator=(Any_Virtual const& other){
Clear();
storage_ = other.storage_->clone();
return *this;
}
template<class T>
Any_Virtual& operator=(T const& t){
Clear();
storage_ = new holder<T>(t);
return *this;
}
void Clear(){
if(storage_)
delete storage_;
}
template<class T>
T& As(){
return static_cast<holder<T>*>(storage_)->held_;
}
private:
holder_base* storage_;
};
// the following demonstrates the use of void pointers
// and function pointers to templated operate functions
// to safely hide the type
enum Operation{
CopyTag,
DeleteTag
};
template<class T>
void Operate(void*const& in, void*& out, Operation op){
switch(op){
case CopyTag:
out = new T(*static_cast<T*>(in));
return;
case DeleteTag:
delete static_cast<T*>(out);
}
}
class Any_VoidPtr{
public:
Any_VoidPtr()
: object_(0)
, operate_(0)
{}
Any_VoidPtr(Any_VoidPtr const& other)
: object_(0)
, operate_(other.operate_)
{
if(other.object_)
operate_(other.object_, object_, CopyTag);
}
template<class T>
Any_VoidPtr(T const& t)
: object_(new T(t))
, operate_(&Operate<T>)
{}
~Any_VoidPtr(){
Clear();
}
Any_VoidPtr& operator=(Any_VoidPtr const& other){
Clear();
operate_ = other.operate_;
operate_(other.object_, object_, CopyTag);
return *this;
}
template<class T>
Any_VoidPtr& operator=(T const& t){
Clear();
object_ = new T(t);
operate_ = &Operate<T>;
return *this;
}
void Clear(){
if(object_)
operate_(0,object_,DeleteTag);
object_ = 0;
}
template<class T>
T& As(){
return *static_cast<T*>(object_);
}
private:
typedef void (*OperateFunc)(void*const&,void*&,Operation);
void* object_;
OperateFunc operate_;
};
int main(){
Any_Virtual a = 6;
std::cout << a.As<int>() << std::endl;
a = std::string("oh hi!");
std::cout << a.As<std::string>() << std::endl;
Any_Virtual av2 = a;
Any_VoidPtr a2 = 42;
std::cout << a2.As<int>() << std::endl;
Any_VoidPtr a3 = a.As<std::string>();
a2 = a3;
a2.As<std::string>() += " - again!";
std::cout << "a2: " << a2.As<std::string>() << std::endl;
std::cout << "a3: " << a3.As<std::string>() << std::endl;
a3 = a;
a3.As<Any_Virtual>().As<std::string>() += " - and yet again!!";
std::cout << "a: " << a.As<std::string>() << std::endl;
std::cout << "a3->a: " << a3.As<Any_Virtual>().As<std::string>() << std::endl;
std::cin.get();
}
shared_ptr
的实际类型不会反映这一点,它总是相同的,例如shared_ptr<int>
,不像标准容器。 - XeoAs
(s)函数不会以这种方式实现。就像我说的,绝不是安全使用的! :) - Xeofunction
,shared_ptr
,any
等?它们都采用类型抹除技术来为用户提供更加便利的使用体验。 - Xeo