MyTinySTL icon indicating copy to clipboard operation
MyTinySTL copied to clipboard

对construct的可变参数模板进行更新

Open David1West opened this issue 2 years ago • 1 comments

(新手观点,大佬轻喷) 目前版本的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可变参数模板: image-20230114233907408

解决方法:

在/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

David1West avatar Jan 15 '23 08:01 David1West

可以只留下这个修改过的 construct 重载,把另外几个重载移除吗?参考 C++11 的设计

(题外话: MyTinySTL 还没有加入 allocator_traits ,如果有的话 allocator 就不需要 construct 成员了。)

forward<Args>通过引用折叠实现了实参类型的完美转发,所以const T &和T &&类型的重载可以移除,指针类型T *的重载不太确定(关于指针实参是否存在类似于引用折叠的机制不是很清楚),但是vscode源码里的new_allocator.h只有这一个可变参数模板的版本

David1West avatar Jan 18 '23 12:01 David1West