MyTinySTL
MyTinySTL copied to clipboard
对construct的可变参数模板进行更新
(新手观点,大佬轻喷) 目前版本的construct的可变参数模板版本并不能有效地让自定义类通过construct来创建对象,如: 对于如下代码:
#include<iostream>
#include"allocator.h"
using namespace std;
class A{
public:
A(int a):m(a),n(0.0){
cout<<"int constructor"<<endl;
}
A(int a,double b):m(a),n(b){
cout<<"int and double constructor"<<endl;
}
int m;
double n;
};
int main(){
mstl::allocator<A> a;
A* p1=a.allocate(10);
a.construct(p1,10);//construct的模板参数包匹配的类型为int,相当于construct(A*,int && args)
a.construct(p1,10,1.0);//相当于construct(A*,int &&,double &&),此时无法调用自己写的forward
a.deallocate(p1);
return 0;
}
如果construct.h中存在
template<typename T,typename ...Args> void construct(T* ,Args && ...args),那么会导致a.construct(p1,10)无法正确匹配到util.h中的construct(Ty1 *,const Ty2&value),a.construct(p1,10,1.0)会报no matching function的错误 如果将上述代码从construct.h中删掉,那么第20行代码会调用A(int)构造函数,打印出"int constructor" 第21行代码则会因为没有对应版本的forward<T>而报错 但是std::allocator是可以实现上述功能的:
#include<iostream>
#include"allocator.h"
using namespace std;
class A{
public:
A(const int& a):m(a),n(0.0){
cout<<"int constructor"<<endl;
}
A(int && a):m(mstl::move(a)),n(0.0){
cout<<"int move constructor"<<endl;
}
A(const int& a,const double &b):m(a),n(b){
cout<<"int and double constructor"<<endl;
}
int m;
double n;
};
int main(){
allocator<A> a;
A* p1=a.allocate(10);
int b=10;
a.construct(p1,20);//打印"int constructor"
a.construct(p1,10,1.0);//打印"int and double constructor"
cout<<p1->m<<endl;//10
a.deallocate(p1,10);
return 0;
}
通过调试,发现上述代码在执行到construct(p1,10,0.1)时,跳转到了new_allocator.h里的construct可变参数模板:
解决方法:
在/allocator.h的allocator结构体内部的construct()直接使用::new分配空间和调用对象的构造函数,
template<class T>
template<typename Other,typename ... Args>
void allocator<T>::construct(Other* p,Args && ... args){
::new ((void*)p) Other(mstl::forward<Args>(args)...);
}
事实上,无需提供三个版本的construct()(通过调用util.h里的重载的construct函数),在allocator里仅需一个上述的construct可变参数模板,也可以满足很多情况的使用:
...
int main(){
mstl::allocator<A> a;/《?》
A* p1=a.allocate(10);
a.construct(p1,10);//int move constructor
a.construct(p1,10,1.0);//int and double constructor
int n1=10;
double n2=2.0;
a.construct(p1,n1,n2);//int and double constructor
a.deallocate(p1,10);
return 0;
}
执行结果如下:
int move constructor
int and double constructor
int and double constructor
可以只留下这个修改过的
construct
重载,把另外几个重载移除吗?参考 C++11 的设计。(题外话: MyTinySTL 还没有加入
allocator_traits
,如果有的话allocator
就不需要construct
成员了。)
forward<Args>通过引用折叠实现了实参类型的完美转发,所以const T &和T &&类型的重载可以移除,指针类型T *的重载不太确定(关于指针实参是否存在类似于引用折叠的机制不是很清楚),但是vscode源码里的new_allocator.h只有这一个可变参数模板的版本