当前位置: 首页 > news >正文

网站可以做二维码导航搜索引擎在线

网站可以做二维码导航,搜索引擎在线,网站建设国标行业分类,2023年长春疫情文章目录 一、类型擦除的作用二、常见的类型擦除容器1.std::any2.std::function3.std::shared_ptr\<void\>和 std::unique_ptr\<void\>4.总结 三、实现一个any参考 类型擦除&#xff08;Type Erasure&#xff09;是一种编程技术&#xff0c;通过它可以在运行时存储…

文章目录

  • 一、类型擦除的作用
  • 二、常见的类型擦除容器
    • 1.std::any
    • 2.std::function
    • 3.std::shared_ptr\<void\>和 std::unique_ptr\<void\>
    • 4.总结
  • 三、实现一个any
  • 参考

类型擦除(Type Erasure)是一种编程技术,通过它可以在运行时存储和操作不同类型的对象,同时隐藏这些类型的具体信息。C++ 标准库中提供了几种常见的类型擦除容器,用于存储和操作多态对象。这些容器包括但不限于以下几种:

  • std::any(C++17 引入)
  • std::function
  • std::shared_ptr 和 std::unique_ptr
  • boost::any(Boost 库)
  • boost::function(Boost 库)

一、类型擦除的作用

类型擦除就是将原有类型消除或者隐藏。

为什么要擦除类型?因为很多时候我不关心具体类型是什么或者根本就不需要这个类型,通过类型擦除我们可以获取很多好处,比如使得我们的程序有更好的扩展性、还能消除耦合以及消除一些重复行为,使程序更加简洁高效。

归纳一下c++中类型擦除方式主要有如下五种:

  • 第一种:通过多态来擦除类型
  • 第二种:通过模板来擦除类型
  • 第三种:通过某种容器来擦除类型
  • 第四种:通过某种通用类型来擦除类型
  • 第五种:通过闭包来擦除类型

第一种类型隐藏的方式最简单也是我们经常用的,通过将派生类型隐式转换成基类型,再通过基类去多态的调用行为,在这种情况下,我不用关心派生类的具体类型,我只需要以一种统一的方式去做不同的事情,所以就把派生类型转成基类型隐藏起来,这样不仅仅可以多态调用还使我们的程序具有良好的可扩展性。然而这种方式的类型擦除仅仅是部分的类型擦除,因为基类型仍然存在,而且这种类型擦除的方式还必须是继承方式的才可以,而且继承使得两个对象强烈的耦合在一起了,正是因为这些缺点,通过多态来擦除类型的方式有较多局限性效果也不好。

时我们通过第二种方式擦除类型,以解决第一种方式的一些缺点。通过模板来擦除类型,本质上是把不同类型的共同行为进行了抽象,这时不同类型彼此之间不需要通过继承这种强耦合的方式去获得共同的行为了,仅仅是通过模板就能获取共同行为,降低了不同类型之间的耦合,是一种很好的类型擦除方式。然而,第二种方式虽然降低了对象间的耦合,但是还有一个问题没解决,就是基本类型始终需要指定,并没有消除基本类型,例如,我不可能把一个T本身作为容器元素,必须在容器初始化时就要知名这个T是具体某个类型。

需要注意的是,第四和第五种方式虽然解决了第三种方式不能彻底消除基本类型的缺点,但是还存一个缺点,就是取值的时候仍然依赖于具体类型,无论我是通过get还是any_cast,我都要T的具体类型,这在某种情况下仍然有局限性。

eg:我有A、B、C、D四种结构体,每个结构体中有某种类型的指针,名称且称为info,我现在提供了返回这些结构体的四个接口供外接使用,有可能是c#或者dephi调用这些接口,由于结构体中的info指针是我分配的内存,所以我必须提供释放这些指针的接口。代码如下:

struct A
{int* info;int id;
};struct B
{double* info;int id;
};struct C
{char* info;int id;
};struct D
{float* info;int id;
};//对外提供的删除接口
void DeleteA(A& t)
{delete t.info;
}void DeleteB(B& t)
{delete t.info;
}void DeleteC(C& t)
{delete t.info;
}void DeleteD(D& t)
{delete t.info;
}

大家可以看到,增加的四个删除函数内部都是重复代码,本来通过模板函数一行搞定,但是没办法,c#可没有c++的模板,还得老老实实的提供这些重复行为的接口,而且这种方式还有个坏处就是每增加一种类型就得增加一个重复的删除接口,怎么办?能统一成一个删除接口吗?可以,一个可行的办法就是将分配的内存通过一个ID关联并保存起来,让外接传一个ID,告诉我要删那块内存,新的统一删除函数可能是这样:

//内部将分配的内存存到map中,让外面传ID,内部通过ID去删除对应的内存块
map<int, T> mapT;template<typename R, typename T>
R GetT()
{R result{1,new T()};mapT.insert(std::pair<int, T>(1, R)); return result;
}//通过ID去关联我分配的内存块,外面传ID,内部通过ID去删除关联的内存块
void DeleteT(const int& id)
{R t = mapT[id]->second();delete t.info;
}

很遗憾,上面的代码编译不过,因为,map<int, T> mapT只能保存一种类型的对象,无法把分配的不同类型的对象保存起来,我们可以通过方式三和方式四,用variant或者any去擦除类型,解决T不能代表多种类型的问题,第一个问题解决。但是还有第二个问题,DeleteT时,从map中返回的variant或者any,无法取出来,因为接口函数中没有类型信息,而取值方法get和any_cast都需要一个具体类型。似乎进入了死胡同,无法只提供一个删除接口了。但是办法总还是有的。

方式五隆重登场了,看似无解的问题,通过方式五就能解决了。通过闭包来擦除类型很好很强大。

闭包也可以称为匿名函数或者lamda表达式,c++11中的lamda表达式就是c++中的闭包,c++11引入lamda,实际上引入了函数式编程的概念,函数式编程有很多优点,使代码更简洁,而且声明式的编码方式更贴近人的思维方式。函数式编程在更高的层次上对不同类型的公共行为进行了抽象,从而使我们不必去关心具体类型。

std::map < int, std::function <void()>> m_freeMap; //保存返回出去的内存块template<typename R, typename T>
R GetResult()
{R result = GetTable<R, T>();    m_freeMap.insert(std::make_pair(result.sequenceId, [this, result]{FreeResult(result);        }));
}bool FreeResultById(int& memId){auto it = m_freeMap.find(memId);if (it == m_freeMap.end())return false;it->second(); //delete by lamdam_freeMap.erase(memId);return true;}

通过闭包去擦除类型,可以解决前面四种擦除方式遇到的问题

二、常见的类型擦除容器

1.std::any

std::any 是 C++17 引入的一种类型擦除容器,可以存储任何类型的值,并在运行时决定具体类型。

#include <any>
#include <iostream>
#include <string>int main() {std::any a = 42;std::cout << std::any_cast<int>(a) << std::endl;a = std::string("Hello, World!");std::cout << std::any_cast<std::string>(a) << std::endl;return 0;
}

2.std::function

std::function 是一种通用的多态函数封装器,可以存储和调用任何可调用对象,包括函数指针、lambda 表达式、绑定表达式或其他函数对象。

#include <functional>
#include <iostream>void foo() {std::cout << "foo" << std::endl;
}int main() {std::function<void()> f = foo;f();f = []() { std::cout << "lambda" << std::endl; };f();return 0;
}

3.std::shared_ptr<void>和 std::unique_ptr<void>

智能指针 std::shared_ptr 和 std::unique_ptr 可以通过指向 void 类型来实现类型擦除,从而存储任意类型的对象。

#include <iostream>
#include <memory>int main() {std::shared_ptr<void> p = std::make_shared<int>(42);std::cout << *std::static_pointer_cast<int>(p) << std::endl;p = std::make_shared<std::string>("Hello, World!");std::cout << *std::static_pointer_cast<std::string>(p) << std::endl;return 0;
}

4.总结

这些类型擦除容器在需要存储和操作多态对象时非常有用,可以在运行时决定具体类型,而不需要在编译时知道类型的具体信息。根据具体需求和项目依赖,可以选择使用标准库或 Boost 库提供的类型擦除容器。

三、实现一个any

any的设计思路:Any内部维护了一个基类指针,通过基类指针擦除具体类型,any_cast时再通过向下转型获取实际数据。当转型失败时打印详情。

any能容纳所有类型的数据,因此当赋值给any时,需要将值的类型擦除才行,即以一种通用的方式保存所有类型的数据。这里可以通过继承去擦除类型,基类是不含模板参数的,派生类中才有模板参数,这个模板参数类型正是赋值的类型,在赋值时,将创建的派生类对象赋值给基类指针,基类的派生类中携带了数据类型,基类只是原始数据的一个占位符,通过多态,它擦除了原始数据类型,因此,任何数据类型都可以赋值给他,从而实现了能存放所有类型数据的目标。

当取数据时需要向下转换成派生类型来获取原始数据,当转换失败时打印详情,并抛出异常。

由于any赋值时需要创建一个派生类对象,所以还需要管理该对象的生命周期,这里用unique_ptr智能指针去管理对象的生命周期。

#include <iostream>
#include <string>
#include <memory>
#include <typeindex>
struct Any
{Any(void) : m_tpIndex(std::type_index(typeid(void))){}Any(const Any& that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex) {}Any(Any && that) : m_ptr(std::move(that.m_ptr)), m_tpIndex(that.m_tpIndex) {}//创建智能指针时,对于一般的类型,通过std::decay来移除引用和cv符,从而获取原始类型template<typename U, class = typename std::enable_if<!std::is_same<typename std::decay<U>::type, Any>::value, U>::type> Any(U && value) : m_ptr(new Derived < typename std::decay<U>::type>(forward<U>(value))),m_tpIndex(type_index(typeid(typename std::decay<U>::type))){}bool IsNull() const { return !bool(m_ptr); }template<class U> bool Is() const{return m_tpIndex == type_index(typeid(U));}//将Any转换为实际的类型template<class U>U& AnyCast(){if (!Is<U>()){cout << "can not cast " << typeid(U).name() << " to " << m_tpIndex.name() << endl;throw bad_cast();}auto derived = dynamic_cast<Derived<U>*> (m_ptr.get());return derived->m_value;}Any& operator=(const Any& a){if (m_ptr == a.m_ptr)return *this;m_ptr = a.Clone();m_tpIndex = a.m_tpIndex;return *this;}private:struct Base;typedef std::unique_ptr<Base> BasePtr;struct Base{virtual ~Base() {}virtual BasePtr Clone() const = 0;};template<typename T>struct Derived : Base{template<typename U>Derived(U && value) : m_value(forward<U>(value)) { }BasePtr Clone() const{return BasePtr(new Derived<T>(m_value));}T m_value;};BasePtr Clone() const{if (m_ptr != nullptr)return m_ptr->Clone();return nullptr;}BasePtr m_ptr;std::type_index m_tpIndex;
};

测试:

void TestAny()
{Any n;    auto r = n.IsNull();//truestring s1 = "hello";n = s1;n = "world";n.AnyCast<int>(); //can not cast int to stringAny n1 = 1;n1.Is<int>(); //true
}

其他:

template<typename T>
T& any_cast(any& aAny)
{if (typeid(T) == aAny.type_){return *static_cast<T*>(aAny.data_);}else{throw std::bad_any_cast{};}
}

参考

  • (原创)c++中的类型擦除
  • How std::any Works

文章转载自:
http://abseil.spbp.cn
http://videophone.spbp.cn
http://shmear.spbp.cn
http://ruijin.spbp.cn
http://crucifixion.spbp.cn
http://gruntle.spbp.cn
http://eyestrings.spbp.cn
http://daric.spbp.cn
http://housemasterly.spbp.cn
http://genic.spbp.cn
http://inkfish.spbp.cn
http://knoxville.spbp.cn
http://teemless.spbp.cn
http://exuviate.spbp.cn
http://parasitism.spbp.cn
http://pillion.spbp.cn
http://transferor.spbp.cn
http://impede.spbp.cn
http://periodontology.spbp.cn
http://hanseatic.spbp.cn
http://siliceous.spbp.cn
http://convect.spbp.cn
http://gastrolith.spbp.cn
http://polyembryony.spbp.cn
http://righteousness.spbp.cn
http://bordure.spbp.cn
http://gatorade.spbp.cn
http://dubiously.spbp.cn
http://oviduct.spbp.cn
http://illegitimate.spbp.cn
http://dynamist.spbp.cn
http://pseudoscope.spbp.cn
http://introgression.spbp.cn
http://negotiation.spbp.cn
http://gloominess.spbp.cn
http://municipally.spbp.cn
http://grutten.spbp.cn
http://desirability.spbp.cn
http://cognisance.spbp.cn
http://embosk.spbp.cn
http://carbonara.spbp.cn
http://chock.spbp.cn
http://enema.spbp.cn
http://cyproheptadine.spbp.cn
http://clithral.spbp.cn
http://cotype.spbp.cn
http://carbonatation.spbp.cn
http://misconstrue.spbp.cn
http://lotic.spbp.cn
http://rhinoscopy.spbp.cn
http://homologate.spbp.cn
http://twelfthly.spbp.cn
http://liquidly.spbp.cn
http://accommodate.spbp.cn
http://acclimatization.spbp.cn
http://jackfield.spbp.cn
http://announceable.spbp.cn
http://suk.spbp.cn
http://profanely.spbp.cn
http://lupercal.spbp.cn
http://neurectomy.spbp.cn
http://pyrrhonist.spbp.cn
http://copywriter.spbp.cn
http://quisle.spbp.cn
http://volubility.spbp.cn
http://prognathous.spbp.cn
http://loveboats.spbp.cn
http://investment.spbp.cn
http://bloodwort.spbp.cn
http://amendment.spbp.cn
http://ejector.spbp.cn
http://phenomenalise.spbp.cn
http://fibrination.spbp.cn
http://episternum.spbp.cn
http://megalocephalous.spbp.cn
http://dissolve.spbp.cn
http://subconscious.spbp.cn
http://ventilate.spbp.cn
http://beirut.spbp.cn
http://collyweston.spbp.cn
http://mangalore.spbp.cn
http://superstitiously.spbp.cn
http://fowler.spbp.cn
http://spinthariscope.spbp.cn
http://bine.spbp.cn
http://chafe.spbp.cn
http://canoness.spbp.cn
http://tostada.spbp.cn
http://driftage.spbp.cn
http://discussant.spbp.cn
http://utilitarianism.spbp.cn
http://illy.spbp.cn
http://almuce.spbp.cn
http://unshakably.spbp.cn
http://sanctorium.spbp.cn
http://dimple.spbp.cn
http://officiant.spbp.cn
http://landscapist.spbp.cn
http://luing.spbp.cn
http://scaffold.spbp.cn
http://www.hrbkazy.com/news/61790.html

相关文章:

  • 北京网站建设哪家公司好小红书seo排名规则
  • 大连网站网站建设奉化seo页面优化外包
  • wordpress ping地址商品seo关键词优化
  • 俄罗斯网站模版百度推广后台登录页面
  • 苏州怎么政府网站建设品牌推广的渠道有哪些
  • 企业网站分析报告seo研究中心超逸seo
  • 星沙网站制作找文网客服联系方式
  • 幼儿园网站建设实践研究国产免费crm系统有哪些在线
  • 做外贸的人如何上国外网站十八大禁用黄app入口
  • 网站模板设计定制化服务吉林seo基础知识
  • 海南的房产网站建设广州seo托管
  • 昆明企业网站建设公司今日军事新闻最新消息
  • 南阳网站建设制作无锡seo网站排名
  • 网站备案安全承诺书广州网站优化公司
  • 大连坐做网站公司全网营销平台有哪些
  • 深圳网络做网站百度发布
  • 做电商的几个网站吗站长工具app下载
  • b2b 网站建设北京外贸网站优化
  • 郑州大学现代远程教育《网页设计与网站建设》课程考核要求中国职业培训在线官网
  • 怎么做网站外推怎么自己做个网站
  • 哪个网站可以做司考题seo外包公司哪家专业
  • 网上做网站广告投放seo免费课程视频
  • 八宝山网站建设徐州百度运营中心
  • 网站色调红黑网络推广和网站推广平台
  • 南京自助建站注册网站怎么注册
  • 做宣传册参考的网站广告公司注册
  • 东莞微网站建设多少钱品牌宣传如何做
  • 青岛的网站设计好的在线crm系统
  • 网页设计费用标准深圳seo优化seo优化
  • 爱妮微如何做网站链接的网址篮网目前排名