Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

David1West
Copy link

(新手观点,大佬轻喷)
目前版本的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

(新手观点,大佬轻喷)
目前版本的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](C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20230114233907408.png)

解决方法:

在/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
~~~
Copy link
Contributor

@frederick-vs-ja frederick-vs-ja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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

@David1West David1West closed this Jan 18, 2023
@David1West
Copy link
Author

David1West commented Jan 18, 2023

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

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

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

@David1West David1West reopened this Jan 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants