types擦除技术

(对于types擦除,我的意思是隐藏部分或全部关于类的types信息,有点像Boost.Any 。)
我想得到一种types的删除技术,同时也分享这些,我知道的。 我希望find一些疯狂的技术,有人在最黑暗的时刻想到。 🙂

我所知道的第一个也是最明显也是最常用的方法是虚拟function。 只需在基于类的层次结构中隐藏类的实现即可。 许多Boost库都这样做,例如Boost.Any这样做是为了隐藏你的types,而Boost.Shared_ptr这样做是为了隐藏(de)分配机制。

然后有模板函数的函数指针的选项,同时将实际的对象保存在void*指针中,就像Boost.Function一样,可以隐藏函数的实际types。 示例实现可以在问题的最后find。

所以,对于我的实际问题:
你知道什么其他types的擦除技术? 如有可能,请提供示例代码,用例,与他们的经验以及可能的链接以供进一步阅读。

编辑
(因为我不确定这个答案是否会增加,或者只是编辑问题,我只会做更安全的问题。)
另一个很好的技术,隐藏实际types的东西, 没有虚函数或void*摆弄,是一个GMan在这里雇用,与我的问题是如何工作的。


示例代码:

 #include <iostream> #include <string> // NOTE: The class name indicates the underlying type erasure technique // this behaves like the Boost.Any type wrt 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(); } 

C ++中所有types的擦除技术都是用函数指针(行为)和void* (用于数据)完成的。 “不同的”方法在添加语义糖的方式上有所不同。 虚拟function,例如,仅仅是语义上的糖

 struct Class { struct vtable { void (*dtor)(Class*); void (*func)(Class*,double); } * vtbl }; 

喵:函数指针。

也就是说,我特别喜欢一种技术:它是shared_ptr<void> ,仅仅是因为它让人不知道你可以这样做:你可以将任何数据存储在shared_ptr<void> ,并且仍然有正确的析构函数在最后调用,因为shared_ptr构造函数是一个函数模板,并且将使用默认情况下为创builddeleter传递的实际对象的types:

 { const shared_ptr<void> sp( new A ); } // calls A::~A() here 

当然,这只是通常的void* / function-pointertypes的擦除,但是非常方便的打包。

从根本上说,这些是你的select:虚函数或函数指针。

如何存储数据并将其与函数关联可能会有所不同。 例如,你可以存储一个指向基类的指针,派生类包含数据虚拟函数的实现,或者你可以将数据存储在其他地方(例如在一个单独分配的缓冲区中),只需派生类提供虚拟函数的实现,其中有一个void*指向数据。 如果将数据存储在单独的缓冲区中,则可以使用函数指针而不是虚函数。

如果有多个操作要应用到types擦除的数据,即使数据是单独存储的,存储指向基础的指针也能很好地工作。 否则,最终会有多个函数指针(每个types擦除函数都有一个指针),或者带有指定要执行的操作的参数的函数。

我也会考虑(类似于void* )使用“raw storage”: char buffer[N]

在C ++ 0x你有std::aligned_storage<Size,Align>::type为此。

你可以在里面存储任何你想要的东西,只要它足够小,并且正确地处理alignment。

请看这一系列的post,查找一个(相当短的)types删除技术列表和关于权衡的讨论: 第一部分 , 第二部分 , 第三部分 , 第四部分 。

我还没有看到提到的那个是Adobe.Poly和Boost.Variant ,这在某种程度上可以被认为是一种types的擦除。

C ++编程语言(第四版)§25.3中的Stroustrup指出:

使用单个runt-time表示来表示多个types的值并依靠(静态)types系统确保它们仅根据其声明的types使用的技术的变体被称为types擦除

特别是,如果使用模板,则不需要使用虚函数或函数指针来执行types擦除。 已经在其他答案中提到过,根据存储在std::shared_ptr<void>的types正确析构函数调用的情况就是一个例子。

Stroustrup书中提供的例子同样令人愉快。

考虑实现template<class T> class Vector ,这是一个沿着std::vector的容器。 当你将使用你的Vector与许多不同的指针types,因为它经常发生,编译器会为每个指针types生成不同的代码。

通过为void*指针定义Vector的专门化,然后将此专用化用作Vector<T*>的所有其他types的通用基本实现,可以防止此代码膨胀 T

 template<typename T> class Vector<T*> : private Vector<void*>{ // all the dirty work is done once in the base class only public: // ... // static type system ensures that a reference of right type is returned T*& operator[](size_t i) { return reinterpret_cast<T*&>(Vector<void*>::operator[](i)); } }; 

正如你所看到的,我们有一个强types的容器,但Vector<Animal*>Vector<Dog*>Vector<Cat*> ,…将共享相同的(C ++ 二进制)代码,指针types擦除 void*后面。

如Marc所述,可以使用cast std::shared_ptr<void> 。 例如,将types存储在函数指针中,将其转换并存储在一个types的函数中:

 #include <iostream> #include <memory> #include <functional> using voidFun = void(*)(std::shared_ptr<void>); template<typename T> void fun(std::shared_ptr<T> t) { std::cout << *t << std::endl; } int main() { std::function<void(std::shared_ptr<void>)> call; call = reinterpret_cast<voidFun>(fun<std::string>); call(std::make_shared<std::string>("Hi there!")); call = reinterpret_cast<voidFun>(fun<int>); call(std::make_shared<int>(33)); call = reinterpret_cast<voidFun>(fun<char>); call(std::make_shared<int>(33)); // Output:, // Hi there! // 33 // ! }