C++ 在没有默认(空)构造函数的情况下,如何允许模板化向量允许类型?
我正在创建一个模板化的Vector类,但是,当将它的用法与std::Vector等比较时,我注意到它不允许没有默认(emtpty)构造函数的structs\classes。我将得到的错误是C++ 在没有默认(空)构造函数的情况下,如何允许模板化向量允许类型?,c++,templates,vector,constructor,C++,Templates,Vector,Constructor,我正在创建一个模板化的Vector类,但是,当将它的用法与std::Vector等比较时,我注意到它不允许没有默认(emtpty)构造函数的structs\classes。我将得到的错误是 error C2512: 'SomeStruct' : no appropriate default constructor available : while compiling class template member function 'Vector<Type>::Vector(voi
error C2512: 'SomeStruct' : no appropriate default constructor available
: while compiling class template member function 'Vector<Type>::Vector(void)'
: see reference to class template instantiation 'Vector<Type>' being compiled
错误C2512:“SomeStruct”:没有合适的默认构造函数可用
:编译类模板成员函数“Vector::Vector(void)”时
:请参阅正在编译的类模板实例化“Vector”的参考
然而,如果我去使用std::vector,这是允许的。这是我的测试用例
struct SomeStruct
{
SomeStruct(int a){}
};
template<typename Type>
class Vector
{
public:
Vector();
protected:
Type* m_Data;
unsigned int m_Count;
unsigned int m_Capacity;
};
template<typename Type>
Vector<Type>::Vector()
{
m_Capacity = 0;
m_Count = 0;
m_Data = new Type[m_Capacity];
}
void main()
{
Vector<SomeStruct> test1;
}
struct SomeStruct
{
SomeStruct(inta){}
};
模板
类向量
{
公众:
向量();
受保护的:
类型*m_数据;
无符号整数m_计数;
无符号整数m_容量;
};
模板
向量::向量()
{
m_容量=0;
m_计数=0;
m_数据=新型[m_容量];
}
void main()
{
向量test1;
}
在没有默认(空)构造函数的情况下,如何允许模板化向量允许类型
(我知道我可以只使用std::vector,但我这样做是为了学习更多关于该语言的知识,并遇到类似的情况)将此内容放入您的代码中:
SomeStruct() = default;
这将创建默认构造函数
或者这个:
SomeStruct() {}
同样的事情。将此内容放入您的代码中:
SomeStruct() = default;
这将创建默认构造函数
或者这个:
SomeStruct() {}
同样的事情。如果删除
m_数据=新类型[m_容量]从构造函数中选择code>,并将此创建延迟到以后,它将工作。然而,正如已经指出的,std::vector
具有相同的规则:如果您有std::vector test1(10)代码>您将得到相同的错误
另外,void main()
也不好。如果删除m_Data=new Type[m_Capacity],则它应始终至少为int main
从构造函数中选择code>,并将此创建延迟到以后,它将工作。然而,正如已经指出的,std::vector
具有相同的规则:如果您有std::vector test1(10)代码>您将得到相同的错误
另外,void main()
也不好。它至少应该是int main
对于没有默认构造函数的类型,这不起作用的原因是因为这一行:
m_Data = new Type[m_Capacity];
上面这一行基本上做了两件事:分配足够的内存来容纳m_Capacity
类型的实例,然后构造每个类型
,以便它们可以随时使用。由于无法通过此new[]
语法实际提供任何构造函数参数,因此使用此语法时需要默认构造函数
std::vector
(以及其他标准容器)处理这个问题的方法是将内存分配过程和构造过程分离。也就是说,std::vector
通过请求内存中没有“内容”的大块内存来分摊内存分配的成本。然后std::vector
用于直接在该内存中构造对象
所以像这样的事情可能发生在一个std::vector
:
// HUGE SIMPLICATION OF WHAT HAPPENS!!!
// EXPOSITION ONLY!!!
// NOT TO BE USED IN ANY PRODUCTION CODE WHATSOEVER!!!
// (I haven't even considered exception safety, etc.)
template<typename T>
class Vector
{
private:
T* allocate_memory(std::size_t numItems)
{
// Allocates memory without doing any construction
return static_cast<T*>(::operator new(sizeof(T)*numItems));
}
void deallocate_memory()
{
::operator delete(buffer);
}
// ...
public:
void push_back(const T& obj)
{
if(theresNotEnoughRoom()) {
std::size_t newCapacity = calculateNewCapacity();
T* temp = allocate_memory(newCapacity);
copyItemsToNewBuffer(temp);
deallocate_memory(buffer);
buffer = temp;
bufferEnd = temp+newCapacity;
}
new (bufferEnd) T(obj); // Construct a new instance of T at end of buffer.
++bufferEnd;
}
void pop_back()
{
if(size() > 0) {
--bufferEnd;
bufferEnd->~T();
}
}
// ...
private:
T* buffer;
T* bufferEnd;
// ...
};
//将发生的事情大大简化了!!!
//只有博览会!!!
//不用于任何生产代码中!!!
//(我甚至没有考虑过异常安全等)
模板
类向量
{
私人:
T*分配内存(标准::大小\u T numItems)
{
//分配内存而不进行任何构造
返回静态_cast(::运算符new(sizeof(T)*numItems));
}
void deallocate_内存()
{
::运算符删除(缓冲区);
}
// ...
公众:
无效推回(常量T和obj)
{
如果(没有足够的房间()){
std::size\u t newCapacity=calculateNewCapacity();
T*temp=分配内存(新容量);
copyItemsToNewBuffer(温度);
释放内存(缓冲区);
缓冲区=温度;
bufferEnd=温度+新容量;
}
new(bufferEnd)T(obj);//在缓冲区的末尾构造一个新的T实例。
++缓冲端;
}
void pop_back()
{
如果(大小()>0){
--缓冲端;
bufferEnd->~T();
}
}
// ...
私人:
T*缓冲器;
T*bufferEnd;
// ...
};
所以这里发生的事情是,我们假设的Vector
类分配了一块相对较大的内存,然后当项目被推送或插入时,该类会在内存中放置新的内容。因此,这消除了默认构造函数要求,因为除非调用方请求,否则我们实际上不会构造任何对象
正如您已经看到的,一个std::vector
类需要做大量的簿记工作,以使它所做的工作高效、安全。这就是为什么我们敦促人们使用标准容器,而不是推出自己的容器,除非你真的知道自己在做什么。创建一个高效、安全和有用的向量类是一项巨大的任务
要了解其中涉及的内容,请看一篇Bjarne Stroustrup撰写的论文,其中讨论了“简单向量”实现(第3.1节)。您将看到,实现它并不是一件小事。这对于没有默认构造函数的类型不起作用的原因是因为这一行:
m_Data = new Type[m_Capacity];
上面这一行基本上做了两件事:分配足够的内存来容纳m_Capacity
类型的实例,然后构造每个类型
,以便它们可以随时使用。由于无法通过此new[]
语法实际提供任何构造函数参数,因此使用此语法时需要默认构造函数
std::vector
(以及其他标准容器)处理这个问题的方法是将内存分配过程和构造过程分离。也就是说,std::vector
通过请求内存中没有“内容”的大块内存来分摊内存分配的成本。然后std::vector
用于构造
m_Data = new Type[m_Capacity];
int typeSize = sizeof(Type);
char* buffer = new char[typeSize * 2];
Type* typeA = new(buffer) Type(default_value);
Type* typeB = new(&buffer[typeSize]) Type(default_value);