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

南京行业网站建设百度推广托管

南京行业网站建设,百度推广托管,网站建设公司平台咨询电话,html5基础前序文章请看: C模板元编程详细教程(之一) C模板元编程详细教程(之二) C模板元编程详细教程(之三) C模板元编程详细教程(之四) C模板元编程详细教程(之五&…

前序文章请看:
C++模板元编程详细教程(之一)
C++模板元编程详细教程(之二)
C++模板元编程详细教程(之三)
C++模板元编程详细教程(之四)
C++模板元编程详细教程(之五)
C++模板元编程详细教程(之六)
C++模板元编程详细教程(之七)
C++模板元编程详细教程(之八)

多选一结构

这一章我们来看看如何编写一个多选一的结构,STL中提供了std::variant,我们就来实现一个简易版。(注意下面我们实现的variant与STL中的是有区别的,但大体思路是相同的,请读者不要以本节的代码参考使用std::variant)。

我们的诉求是,创建一个数据结构,「可能」存放多种类型,但同一时间只能有一种,并且可以在运行期变化为另一种。这里比较容易想到的做法就是用共合体类型存储数据,再加一个用于表示当前哪种数据是生效的index。请看代码:

template <typename T1, typename T2>
class variant {
public:// 针对每种情况的构造variant(const T1 &t1);variant(const T2 &t2);
private:union {T1 t1;T2 t2;} data;int index; // 当前生效的数据序号
};

只不过这样我们很快就会发现很多严重的问题:

  1. 对于任意个数的参数,无法映射到union结构中;
  2. 如果数据类型不含无参构造函数,union结构的构造会报错;
  3. variant的构造函数依赖数据类型的拷贝构造,对于不可拷贝类型来说无法创建。

因此,我们必须换一个思路。既然一开始我们想到用union,主要也是为了内存空间的复用,所以我们只需要自己来维护一片数据空间就好了,以参数类型中长度最大的为准,创建一个缓存空间即可。

template <typename... Types>
class variant {private:void *data = std::malloc(std::max(sizeof(Types)...)); // 计算出最长的Typeint index; // 当前生效的数据序号
};

接下来的任务就是,构造函数。由于这里的Types是变参,所以无法穷举,与此同时,我们希望可以就地构造,跳过「拷贝构造」这个阶段,以支持不可拷贝类型的数据,因此必须有一个Index来标识,后面跟变参:

template <typename... Types>
class variant {public:template <size_t Index, typename... Args>variant(Args &&... args); // 大概是这个意思private: void *data = std::malloc(std::max(sizeof(Types)...)); // 计算出最长的Typeint index; // 当前生效的数据序号
};

然而,出于语法限制,构造函数用模板生成的话,是无法手动实例化的,例如:

variant<1, int> va; // <1, int>会识别为类型的模板参数,而不是构造函数的模板参数

因此,构造函数的模板参数只能依赖于自动推导,所以,我们就需要提供一个工具,用来「传递」这个Index

template <size_t Index>
struct in_place_index_t {}; // 单纯的静态工具,用于传递Index,没有运行期意义template <size_t Index>
constexpr inline in_place_index_t<Index> in_place_index; // 对应in_place_index_t类型的实例template <typename... Types>
class variant {public:template <size_t Index, typename... Args>variant(const in_place_index_t<Index> &, Args &&... args);private:void *data = std::malloc(std::max(sizeof(Types)...)); int index;
};

然后,构造时,也同样通过in_place_index来构造,传递这个Index

 // 用于测试的类型
struct Test1 {Test1(int, double);
};
struct Test2 {Test1(char, int);
};void Demo() {variant<Test1, Test2> var{in_place_index<0>, 1, 1.5}; // 用于构造Test1类型variant<Test1, Test2> var{in_place_index<1>, 'A', 1}; // 用于构造Test2类型
}

那么接下来的问题就是如何解析了,variant本身的参数是一组typename列表,但构造传进来的是一个Index序号,如何对应呢?相信读者到现在应该已经建立初步的感觉了,没错,递归大法好!

template <size_t Index, typename Head, typename... Args>
struct get_type_by_index : get_type_by_index<Index - 1, Args...> {};template <typename Head, typename... Args>
struct get_type_by_index<0, Head, Args...> {using type = Head;
};// 验证Demo
void Demo() {std::cout << std::is_same_v<typename get_type_by_index<2, int, double, char, void *>::type , char> << std::endl; // 1
}

有了这个工具,我们就可以从类型列表里通过Index取出对应的类型了,以此来完成variant的构造函数:

// 实现构造函数
template <typename... Types>
template <size_t Index, typename... Args>
variant<Types...>::variant(const in_place_index_t<Index> &, Args &&... args): index(Index) {// 先把需要构造的类型拿出来using data_type = typename get_type_by_index<Index, Args...>::type;// 在data处进行就地构造new(data) std::decay_t<data_type>(std::forward<Args>(args)...);
}

最难的构造已经完成了,我们先实现一下周边功能,析构、拷贝构造和赋值函数先放一放,给出一个阶段性的代码:

template <size_t Index>
struct in_place_index_t {}; // 单纯的静态工具,用于传递Index,没有运行期意义template <size_t Index>
constexpr inline in_place_index_t<Index> in_place_index; // 对应in_place_index_t类型的实例template <size_t Index, typename Head, typename... Args>
struct get_type_by_index : get_type_by_index<Index - 1, Args...> {};template <typename Head, typename... Args>
struct get_type_by_index<0, Head, Args...> {using type = Head;
};template <typename... Types>
class variant {public:template <size_t Index, typename... Args>variant(const in_place_index_t<Index> &, Args &&... args);// 获取当前序号int index() const;// 取出数据template <size_t Index>auto get() const -> std::add_lvalue_reference_t<std::decay_t<typename get_type_by_index<Index, Types...>::type>>;private:void *data_ = std::malloc(std::max(sizeof(Types)...)); // 计算出最长的Typeint index_; // 当前生效的数据序号
};// 实现构造函数
template <typename... Types>
template <size_t Index, typename... Args>
variant<Types...>::variant(const in_place_index_t<Index> &, Args &&... args): index_(Index) {// 先把需要构造的类型拿出来using data_type = typename get_type_by_index<Index, Args...>::type;// 在data处进行就地构造new(data_) std::decay_t<data_type>(std::forward<Args>(args)...);
}// 获取序号
template <typename... Types>
int variant<Types...>::index() const {return index_;
}// 取出数据(这个功能在std::variant中其实实现在std::get中)
template <typename... Types>
template <size_t Index>
auto variant<Types...>::get() const -> std::add_lvalue_reference_t<std::decay_t<typename get_type_by_index<Index, Types...>::type>> {using data_type = std::decay_t<typename get_type_by_index<Index, Types...>::type>;return *static_cast<data_type *>(data_);
}

接下来我们要攻克的,就是析构、拷贝构造、拷贝赋值这几个问题了,它们的难点在于,没有静态的Index参数可供使用,但还要找到现在数据的类型。就拿析构来说,析构函数并没有静态的Index参数,只有一个运行时的index成员,但我们要找到此时index_成员表示的data_所指向数据的实际类型,然后调用这个类型的析构函数。拷贝构造、拷贝赋值也同样需要先析构现有数据,所以面临同样的问题。

这种情况怎么办呢?类比前面章节介绍的元组的动态get方法,我们这里也采取「静态穷举生成所有可能情况代码」的方法。

以析构为例,在析构时,要根据index_去判断当前保存的数据是哪一种类型的,然后去调用对应类型的析构函数。因此,我们需要在静态期就提供每一种类型的析构方法,然后将它们和类型的Index关联起来,这样在运行时就可以调用了。

template <typename... Types>
class variant {public:// 无关代码暂时省略 ~variant(); // 析构函数private:void *data_ = std::malloc(std::max(sizeof(Types)...));int index_; // 当前生效的数据序号// 生成每一个Type下对应类型的析构方法template <typename Type>void destory_data();
};template <typename... Types>
template <typename Type>
void variant<Types...>::destory_data() {// 用data_指针按照对应的类型调用析构函数static_cast<std::add_pointer_t<Type>>(data_)->~Type();
}// 实现析构函数
template <typename... Types>
variant<Types...>::~variant() {// 析构函数中,把所有的index对应的destory_data保存下来(编译期完成这样一个映射表)std::array<void (variant<Types...>::*)(), sizeof...(Types)> destory_functions {&variant<Types...>::destory_data<Types>... // 按照类型参数展开,把对应的destory_data实例的函数指针保存下来};// 到了运行期,根据当前实际的index_值,选择对应的析构方法if (data_ != nullptr) {(this->*destory_functions.at(index_))();std::free(data_);}
}

对于拷贝构造、拷贝赋值来说,首先把原来的内容析构调,然后再根据被复制方的index_,来调用对应的构造函数。于是,我们还需要补充一个index_与构造函数的映射表,思路和析构完全相同:

template <typename... Types>
class variant {public:// 无关代码先省略variant(const variant &va);private:void *data_ = std::malloc(std::max(sizeof(Types)...));int index_; // 当前生效的数据序号// 生成每一个Type下对应类型的析构方法template <typename Type>void destory_data();// 生成每一个Type下对应类型的构造方法// 注意,由于我们要把生成的所有方法保存在数组中,因此函数类型需要一致,所以参数用泛型指针template <typename Type>void create_data(const void *obj);
};template <typename... Types>
template <typename Type>
void variant<Types...>::create_data(const void *obj) {// 用data_指针按照对应的类型调用拷贝构造new(data_) Type(*static_cast<const Type *>(obj));
}template <typename... Types>
variant<Types...>::variant(const variant &va): index_(va.index_) {// 静态期保存所有类型的构造函数std::array<void (variant<Types...>::*)(const void *), sizeof...(Types)> create_functions {&variant<Types...>::create_data<Types>...};// 动态时根据index_调用对应的构造(this->*create_functions.at(index_))(va.data_);
}

有了这样的思路,那么我们就可以完善所有的代码了,拷贝赋值函数可以按照相同的方法写出来了(只不过需要先析构,再重新构造)。下面给出整个项目的完整代码:

// 辅助工具
template <size_t Index>
struct in_place_index_t {}; // 单纯的静态工具,用于传递Index,没有运行期意义template <size_t Index>
constexpr inline in_place_index_t<Index> in_place_index; // 对应in_place_index_t类型的实例template <size_t Index, typename Head, typename... Args>
struct get_type_by_index : get_type_by_index<Index - 1, Args...> {};template <typename Head, typename... Args>
struct get_type_by_index<0, Head, Args...> {using type = Head;
};// variant结构的声明
template <typename... Types>
class variant {public:template <size_t Index, typename... Args>variant(const in_place_index_t<Index> &, Args &&... args);variant(const variant &va);variant(variant &&va);~variant();variant &operator =(const variant &va);variant &operator =(variant &&va);// 获取当前序号int index() const;// 取出数据template <size_t Index>auto get() const -> std::add_lvalue_reference_t<std::decay_t<typename get_type_by_index<Index, Types...>::type>>;private:void *data_ = std::malloc(std::max(sizeof(Types)...));int index_; // 当前生效的数据序号// 生成每一个Type下对应类型的析构方法template <typename Type>void destory_data();// 生成每一个Type下对应类型的构造方法template <typename Type>void create_data(const void *obj);
};// 实现构造函数
template <typename... Types>
template <size_t Index, typename... Args>
variant<Types...>::variant(const in_place_index_t<Index> &, Args &&... args): index_(Index) {using data_type = typename get_type_by_index<Index, Args...>::type;new(data_) std::decay_t<data_type>(std::forward<Args>(args)...);
}// 实现析构函数
template <typename... Types>
variant<Types...>::~variant() {// 析构函数中,把所有的index对应的destory_data保存下来(编译期完成这样一个映射表)std::array<void (variant::* const)(), sizeof...(Types)> destory_functions {&variant::destory_data<Types>...};// 到了运行期,根据当前实际的index_值,选择对应的析构方法if (data_ != nullptr) {(this->*destory_functions.at(index_))();std::free(data_);}
}// 实现拷贝构造函数
template <typename... Types>
variant<Types...>::variant(const variant &va): index_(va.index_) {// 静态期保存所有类型的构造函数std::array<void (variant<Types...>::*)(const void *), sizeof...(Types)> create_functions {&variant<Types...>::create_data<Types>...};// 动态时根据index_调用对应的构造(this->*create_functions.at(index_))(va.data_);
}// 实现拷贝赋值函数
template <typename... Types>
variant<Types...> &variant<Types...>::operator =(const variant &va) {// 先析构现有数据std::array<void (variant<Types...>::*)(), sizeof...(Types)> destory_functions {&variant<Types...>::destory_data<Types>...};(this->*destory_functions.at(index_))();// 再重新构造std::array<void (variant<Types...>::*)(const void *), sizeof...(Types)> create_functions {&variant<Types...>::create_data<Types>...};(this->*create_functions.at(index_))(va.data_);return *this;
}// 实现移动构造函数
template <typename... Types>
variant<Types...>::variant(variant &&va): index_(va.index_), data_(va.index_) {// 上面直接浅拷贝即可,然后把被移动的data_置空va.data_ = nullptr;va.index_ = -1; // 标记为不合法值
}// 实现移动赋值函数
template <typename... Types>
variant<Types...> &variant<Types...>::operator =(variant &&va) {// 先析构现有数据std::array<void (variant<Types...>::*)(), sizeof...(Types)> destory_functions {&variant<Types...>::destory_data<Types>...};(this->*destory_functions.at(index_))();// 再浅拷贝data_ = va.data_;index_ = va.index_;// 把被移动的data_置空va.data_ = nullptr;va.index_ = -1; // 标记为不合法值return *this;
}// 私有方法的实现
template <typename... Types>
template <typename Type>
void variant<Types...>::create_data(const void *obj) {// 用data_指针按照对应的类型调用拷贝构造new(data_) Type(*static_cast<const Type *>(obj));
}template <typename... Types>
template <typename Type>
void variant<Types...>::destory_data() {// 用data_指针按照对应的类型调用析构函数static_cast<std::add_pointer_t<Type>>(data_)->~Type();
}// 公有方法的实现
// 获取序号
template <typename... Types>
int variant<Types...>::index() const {return index_;
}// 取出数据(这个功能在std::variant中其实实现在std::get中)
template <typename... Types>
template <size_t Index>
auto variant<Types...>::get() const -> std::add_lvalue_reference_t<std::decay_t<typename get_type_by_index<Index, Types...>::type>> {using data_type = std::decay_t<typename get_type_by_index<Index, Types...>::type>;return *static_cast<data_type *>(data_);
}

小结

这一篇以一个「多选一」结构为例,带大家体验了一下模板元编程的实际使用方式,当然,这还没完,下一篇我们会针对这个多选一结构写一个「成员访问器」,会用到更多模板元编程的技巧。


文章转载自:
http://chinaware.c7493.cn
http://petrologist.c7493.cn
http://eluvium.c7493.cn
http://bon.c7493.cn
http://pleasureless.c7493.cn
http://diandrous.c7493.cn
http://autopista.c7493.cn
http://screenload.c7493.cn
http://luetically.c7493.cn
http://inwind.c7493.cn
http://epileptiform.c7493.cn
http://polyphagy.c7493.cn
http://trapse.c7493.cn
http://quadriceps.c7493.cn
http://transit.c7493.cn
http://hankeringly.c7493.cn
http://bufflehead.c7493.cn
http://encephalization.c7493.cn
http://akkra.c7493.cn
http://investiture.c7493.cn
http://beatitude.c7493.cn
http://orrery.c7493.cn
http://eurystomatous.c7493.cn
http://loftsman.c7493.cn
http://viticulture.c7493.cn
http://knurled.c7493.cn
http://mx.c7493.cn
http://chevet.c7493.cn
http://calaboose.c7493.cn
http://withdraw.c7493.cn
http://suffice.c7493.cn
http://entries.c7493.cn
http://flavourous.c7493.cn
http://totalise.c7493.cn
http://salmo.c7493.cn
http://phlebitis.c7493.cn
http://acrylate.c7493.cn
http://trifocal.c7493.cn
http://loo.c7493.cn
http://universalize.c7493.cn
http://synapse.c7493.cn
http://nymphish.c7493.cn
http://lipocyte.c7493.cn
http://icarus.c7493.cn
http://selenite.c7493.cn
http://malentendu.c7493.cn
http://tensity.c7493.cn
http://pipefish.c7493.cn
http://ferrel.c7493.cn
http://scotchman.c7493.cn
http://demandant.c7493.cn
http://hyperopia.c7493.cn
http://cytotech.c7493.cn
http://aztecan.c7493.cn
http://bacteriostasis.c7493.cn
http://kitchener.c7493.cn
http://jumeau.c7493.cn
http://algonquin.c7493.cn
http://turpitude.c7493.cn
http://chloroplatinic.c7493.cn
http://shanghailander.c7493.cn
http://flyness.c7493.cn
http://newfangled.c7493.cn
http://sapiential.c7493.cn
http://drunken.c7493.cn
http://gammer.c7493.cn
http://scordato.c7493.cn
http://menado.c7493.cn
http://bidonville.c7493.cn
http://skiver.c7493.cn
http://dft.c7493.cn
http://ascertain.c7493.cn
http://secreta.c7493.cn
http://cancerization.c7493.cn
http://mecopteran.c7493.cn
http://pacificator.c7493.cn
http://setem.c7493.cn
http://edc.c7493.cn
http://arrogation.c7493.cn
http://dilatability.c7493.cn
http://asepsis.c7493.cn
http://greenway.c7493.cn
http://budgerigar.c7493.cn
http://sapful.c7493.cn
http://phanerogamic.c7493.cn
http://dilettantism.c7493.cn
http://infieldsman.c7493.cn
http://scordatura.c7493.cn
http://pyoid.c7493.cn
http://pubescence.c7493.cn
http://unattained.c7493.cn
http://tig.c7493.cn
http://rag.c7493.cn
http://saltcat.c7493.cn
http://nenadkevichite.c7493.cn
http://humour.c7493.cn
http://lynx.c7493.cn
http://ostleress.c7493.cn
http://heterotopism.c7493.cn
http://cowslip.c7493.cn
http://www.zhongyajixie.com/news/81459.html

相关文章:

  • 手机 网站开发aspx网络营销策略分析案例
  • 网站seo快速排名seo技术推广
  • wordpress文章对齐放心网站推广优化咨询
  • 网站行业认证怎么做广告推广赚钱在哪接
  • 客户端下载seo查询排名软件
  • 网站建设 职责营销网站大全
  • 手机怎么做自己的网站培训课程开发
  • 百度手机导航官方新版惠州seo公司
  • 企业建站划算吗百度网盘客户端
  • 一个服务器可以做两个网站吗百度移动权重
  • 佛山建网站定制企业培训机构有哪些
  • 杭州网站开发响应式百度云网页版入口
  • 医药加盟网站模板seo诊断工具网站
  • 网站挂马检测流程图推广宣传方式有哪些
  • 天德建设集团网站网站优化怎么做
  • 动态网站和静态网站区别seo策略什么意思
  • 乐搜做网站营销怎么做
  • 太原网站建设的公司排名百度网站安全检测
  • 深圳龙华鸿宇大厦网站建设重庆seo顾问
  • 笔记本做系统哪个网站好学生个人网页制作代码
  • 珠海做网站那家好和业务多一样的平台
  • 手机版网站如何做图片滚动如何让百度收录网址
  • 学做网站好做吗如何发布自己的广告
  • 吉林市做网站哪家好推广公司产品
  • 哈密做网站网站域名查询系统
  • 运输网站建设网站关键词全国各地的排名情况
  • 电子商务从事什么工作百度爱采购优化软件
  • 手机产品 网站建设江苏短视频seo搜索
  • 数码网站建设图片百度竞价推广效果好吗
  • 带做网站疫情最严重的三个省