原文
本文展示一个构造对象
方式,用户无需显式
调用构造器.对有参
构造器类,该实现在构造改对象
时传递默认值
来构造.
当然用户也可指定(绑定
)某个参数的值.实现思路
参考boost-ext/di
的实现.看下示例:
构 成员{
整 x=10;
};
构 成员1{
整 x=11;
};
类 例子1{
公:
例子1(成员 x,成员1 x1){
输出<<x.x<<行尾;//10
输出<<x1.x<<行尾;//11
}
};
整 主(){
动 e1=远前::对象创建者<>().元 创建<例子1>();
}
示例比较简单,构造一个对象创建者
对象,并调用他的创建
来创建一个例子1
的对象,因为使用对象创建者
来构造,所以不需要传递参数
,它会自动构造
.
好处是,构造对象
时,可无需考虑该对象构造器
是几个参数或类型
,想要增加参数
时,无需修改代码
,当然指定参数
的话除外.
该用法
也叫依赖注入
.
还蛮酷炫,看看如何做到的?先来说下主体想法
,首先最重要的当然是对象创建者
,该类如何知道要构造
对象的构造器
的参数类型
是什么呢,知道参数类型
才能构造一个参数
传递,同时参数
也同样需要对象创建者
来构造,依次递归
.
上边说到了有两个
问题要解决,第一个
就是如何识别
构造器的参数类型
,第二个是要构造构造器参数
时,如果递归构造
?
使用任何类型
来识别构造器参数
,简单示例:
构 任何类型{
元<型名 T>
符号 T(){
中 T{};
}
};
构 成员{};
构 例子{
例子(成员 m,整){
}
};
整 主(){
例子(任何类型(),2);
中 0;
}
调用任何类型()
可匹配至任意类型
,然后在构造例子
时,编译器会去找相应类型
来构造.大家可能发现我使用的是多个
参数来举例任何类型
,如果参数是单个任何类型
会有冲突
,因为拷贝构造器
也是一个参数,所以编译器
会识别冲突,该问题后边也要处理
.
类 例子{
公:
例子(成员 m){
输出<<m.x<<行尾;
}
};
整 主(){
例子 e(任何类型{});
中 0;
}
//--------以下报错
注意:候选人:'例子::例子(成员)'
|例子(成员 m){
|^~~~~~~
:注意:候选人:'常式 例子::例子(常 例子&)'
类 例子{
因为构造器
参数可能是个类对象
,该对象的构造器参数
又是其他
类对象,识别类型
后,继续调用函数
来构造该对象
,以此类推.
当然使用过程也不全部是使用默认构造
,可能也需要传递指定参数
与构造器参数
绑定,但是构造器的参数类型
又是多样的.
这里先用元组
来保存,若识别出来的类型
和保存
数据类型是一致
的,则不用构造
而是直接传递该数据
给构造器.
开始写代码,肯定有个任何类型
的类及对象创建者
的类.对象创建者
用来构造对象返回
,会只用任何类型
类来识别
类型.
大概看下具体的实现:
元<型名...O>
类 对象创建者{
公:
元<型名...T>
显 对象创建者(T&&...o):依赖_(前向<T>(o)...){}
//...
私:
元组<常 O&...>依赖_;
};
用元组
保存要绑定
参数时,要保存
数据就得拷贝
,这里为了避免拷贝
,元组
中类型是常
左引用,但这样就得用户自己
来维护要绑定
参数的生命期.
O
是要绑定
参数类型,构造器中为了避免拷贝
,用完美转发
来实现.依赖_
就是保存绑定参数
的数据结构
.
元<型名...O>
类 对象创建者{
//...
元<型名 T>T 创建(){
如 常式((是相同<T,O>::值||...)){
中 取<常 T&>(依赖_);
}
异 如 常式(是可默认构造值<T>){
中 T{};
}
异 如 常式(是可构造<T,任何第一引用类型<对象创建者,T,远前无效,O...>>::值){
中 T{任何第一引用类型<对象创建者,T,远前无效,O...>{本}};
}
异 如 常式(是可构造<T,任何第一类型<对象创建者,T,远前无效,O...>>::值){
中 T{任何第一类型<对象创建者,T,远前无效,O...>{本}};
}
异{
中 创建多参对象<T>(造引序<10>{});
}
}
//...
};
这里就是创建
函数了:
1,首先判断是不是已绑定了要创建的类对象
,如果绑定
了,则直接从元组
中取出返回.
2,未绑定的话,再判断是否可构造
默认构造(即可无参
构造),可以的话返回空对象
.
3,然后判断是不是参数构造器
,参数这里分成了两种
,是引用
类型或非引用
类型.因为,识别T
和T&
会引起冲突,所以分开处理.举例说明:
构 任何类型{
元<型名 T>符号 T(){
中 T{};
}
元<型名 T>符号 T&(){
中 T{};
}
};
类 例子{
公:
例子(成员 m,整){
输出<<m.x<<行尾;
}
};
例子 e(任何类型{},7);
//报错如下:
错误:转换 从'任何类型'到'成员'是 歧义
例子 e(任何类型{},7);
^~~~~~~~~
候选:'任何类型::符号 T()[带 T=成员]'
符号 T(){
^~~~~~~~
注意:候选:'任何类型::符号 T&()[带 T=成员]'
符号 T&(){
4,最后是构造多参
构造器,分开一个
参数和多个
参数的原因是,一个
参数需要处理拷贝构造器
及单参
构造器冲突,按参数给创建多参对象
函数传递了1~10
的整数序列
,表示目前最多只能支持10
个参数的构造器
.
继续看多参
的构造:
元<型名 T,大小型...N>
T 创建多参对象(常 引序<N...>&){
如 常式(是可构造值<T,在<任何引用类型<对象创建者,远前无效,O...>,N>...>){
中 T{在<任何引用类型<对象创建者,远前无效,O...>,N>{本}...};
}
异{
中 创建多参对象<T>(造引序<的大小...(N)-1>{});
}
}
首先判断是否可由多个任何引用类型
类型来构造,尽量,直接构造对象
,否则,就减少参数个数
来重新匹配.
然后再观察如何编写任何类型
,先看任何第一类型
的情况.为了避免和拷贝构造器
冲突,简单优化下:
构 任何第一类型{
元<型名 T,型名=允许如型<!是相同值<源,T>>>
常式 符号 T(){
中 创建者_->元 创建<T>();
}
};
使用替失非错
来先排除构造拷贝器
,用任何第一类型
识别参数类型
时,需要按模版参数
传递要构造的类给源
,让T
与源
不一样,进而告诉编译器
要调用的不是拷贝构造器
而是其他
函数.
创建者_
就是对象创建者
对象,构造
参数递归
调用创建
函数.多参
也是类似
,只是不需要额外判断拷贝构造器
.
还要注意
,如果构造器
类型是引用
类型,在和绑定参数
匹配时,会多一次拷贝,所以还要区分.
元<型名 创建者,型名 源,型名...O>
构 任何第一引用类型{
元<型名 T,型名=允许如型<!是相同值<源,退化型<T>>>,
型名=允许如型<(是相同<退化型<T>,O>::值||...)>>
常式 符号 T&(){
中 常转<T&>(创建者_->元 取依赖<T>());
}
元<型名 T,型名=允许如型<!是相同值<源,退化型<T>>>,
型名=允许如型<(是相同<退化型<T>,O>::值||...)>>
常式 符号 T&&(){
中 静转<T&&>(常转<T&>(创建者_->元 取依赖<T>()));
}
创建者*创建者_=空针;
};
在和绑定参数
匹配,且传递引用
时,单独
实现,直接返回
,而不再调用创建者
的创建
函数,并且强制转化
.多参
类型识别也是类似
.
源码在此