C++ 如何在std::vector中存储对象而不复制或移动构造函数?
为了提高std::vector的效率,它的底层数组需要预先分配,有时需要重新分配。但是,这需要创建并在以后移动C++ 如何在std::vector中存储对象而不复制或移动构造函数?,c++,c++11,vector,constructor,move,C++,C++11,Vector,Constructor,Move,为了提高std::vector的效率,它的底层数组需要预先分配,有时需要重新分配。但是,这需要创建并在以后移动T类型的对象,并使用复制ctor或移动ctor 我遇到的问题是,T无法复制或移动,因为它包含无法复制或移动的对象(例如原子和互斥体)。(是的,我正在实现一个简单的线程池。) 我希望避免使用指针,因为: 我不需要一个间接的层次,所以我不想要一个 (指针的效率较低,并且增加了复杂性。使用指针会增加内存碎片并减少数据局部性,这可能(但不一定)会对性能产生显著影响。虽然不是很重要,但仍然值得考虑
T
类型的对象,并使用复制ctor或移动ctor
我遇到的问题是,T
无法复制或移动,因为它包含无法复制或移动的对象(例如原子
和互斥体
)。(是的,我正在实现一个简单的线程池。)
我希望避免使用指针,因为:
更新:我修正了一些不正确的假设,并根据评论和答案中的反馈重新表述了问题。首先,
std::mutex
无法复制或移动,因此您不得不使用某种间接方式
由于您希望将互斥体存储在向量中,而不是复制它,因此我将使用std::unique\u ptr
vector
不允许某些向量操作(例如针对每个向量)
我不确定我是否理解那句话。完全可以对以下各项执行范围:
std::vector< std::unique_ptr< int > > v;
// fill in the vector
for ( auto & it : v )
std::cout << *it << std::endl;
std::vector>v;
//填充向量
用于(自动和it:v)
std::cout v;
v、 后置炮台(新int(3));
v、 后置炮台(新int(5));
std::for_each(v.begin()、v.end()、[](const std::unique_ptr&it){std::cout
但是,这需要创建具有复制ctor的T类型的对象
这并不完全正确,从C++11开始,如果您使用std::vector
的构造函数,它将默认构造许多元素,那么您就不需要复制或移动构造函数
因此,如果没有从池中添加或删除线程,则可以执行以下操作:
int num = 23;
std::vector<std::mutex> vec(num);
最后,请注意,std::thread
当然是可移动的,因此您可以使用std::vector
+std::vector::emplace_back
。总结到目前为止提出的建议:
使用vector
——添加显式间接级别,这是OP不需要的
使用deque
——我第一次尝试了deque
,但是从中删除对象也不起作用
解决方案是使用单链表(或者如果你想使用双链表,也可以使用)。正如@JanHudec指出的,vector
(以及它的许多朋友)添加或删除项目时需要重新分配。这与不允许复制或移动的对象(如mutex
和atomic
)不匹配。forward\u list
和list
不需要重新分配,因为每个单元格都是独立分配的(我不能引用这方面的标准,但索引方法引起了这一假设)。由于它们实际上是链表,它们不支持随机访问索引。myList.begin()+I
将为您提供I
第四个元素的迭代器,但它(最肯定)必须首先循环所有以前的i
单元格
我还没有看过标准的承诺,但在Windows(Visual Studio)和CompileOnline(g++)上运行良好。请随意在上使用以下测试用例:
我希望它的性能与vector
大致相同(只要不经常使用随机访问索引)
警告:使用转发列表::删除当前在VS 2012中不起作用。这是因为它在尝试删除元素之前复制了元素。头文件Microsoft Visual Studio 11.0\VC\include\forward\u列表
(与列表
中的问题相同)显示:
因此,它被复制“以防一路上被删除”。这意味着list
和forward\u list
甚至不允许存储unique\u ptr
。我认为这是一个设计缺陷
解决方法很简单:如果
,则必须使用remove\u,而不是remove
,因为该函数的实现不会复制任何内容
很多功劳都归功于其他答案。然而,由于没有一个答案是没有指针的完整解决方案,我决定写这个答案。您可以在std::vector
中存储元素,而无需移动或复制构造函数,但您只需避免使用需要元素具有移动或复制构造函数的方法。几乎1任何改变向量大小的操作(例如,向后推
,调整大小()
,等等)
实际上,这意味着您需要在构造时分配一个固定大小的向量,这将调用对象的默认构造函数,您可以通过赋值对其进行修改。这至少适用于可以被赋值的std::atomic
对象
1clear()
是不需要复制/移动构造函数的大小更改方法的唯一示例,因为它从不需要移动或复制任何元素(毕竟,此操作后向量为空)。当然,调用此函数后,您再也不能增长零大小的向量了!如果您可以容忍所有线程池对象都是默认构造的,而不是提供除ctor/dtor之外的其他方法使它们活/死,那么std::array
会起作用吗?我认为for_每个都适合向量成员,只要您是sing reference访问成员。预分配不是问题。内存已预分配,但对象已打开
int num = 23;
std::vector<std::mutex> vec(num);
std::deque<std::mutex> deq;
deq.emplace_back();
deq.emplace_back();
for(auto& m : deq) {
m.lock();
}
#include <forward_list>
#include <iostream>
#include <mutex>
#include <algorithm>
using namespace std;
class X
{
/// Private copy constructor.
X(const X&);
/// Private assignment operator.
X& operator=(const X&);
public:
/// Some integer value
int val;
/// An object that can be neither copied nor moved
mutex m;
X(int val) : val(val) { }
};
int main()
{
// create list
forward_list<X> xList;
// add some items to list
for (int i = 0; i < 4; ++i)
xList.emplace_front(i);
// print items
for (auto& x : xList)
cout << x.val << endl;
// remove element with val == 1
// WARNING: Must not use remove here (see explanation below)
xList.remove_if([](const X& x) { return x.val == 1; });
cout << endl << "Removed '1'..." << endl << endl;
for (auto& x : xList)
cout << x.val << endl;
return 0;
}
Executing the program....
$demo
3
2
1
0
Removed '1'...
3
2
0
void remove(const _Ty& _Val_arg)
{ // erase each element matching _Val
const _Ty _Val = _Val_arg; // in case it's removed along the way
// ...
}