conditional_variable不是CopyConstructible,MoveConstructible,CopyAssignable,MoveAssignable。
我们可以这样打电话吗
vector<conditional_variable> cond;
conditional_variable c1;
conditional_variable c2;
cond.push_back(c1);
cond.push_back(c2);
在这些情景中进行的正确方法是什么
答案 0 :(得分:3)
你可以创建一个可以默认构造的东西的向量,但是不能通过使用带有size参数的构造函数来复制或移动它:
std::vector<std::condition_variable> cv_vec(20);
此类向量无法增长,但可能会因pop_back()
或clear()
(但不是erase()
或resize()
)而缩小。
或者,由于所有内容都可以通过额外的间接级别来解决,因此您可以使用std::unique_ptr<std::condition_variable>
向量。
现在,为什么有人会想要为condition_variable
这样的同步原语执行此操作,我不知道......
答案 1 :(得分:1)
不,你不能,
我建议你使用vector<unique_ptr<conditional_variable>>
。然后,您可以分配新的conditional_variable
并将其添加到您的向量
答案 2 :(得分:0)
您可以通过让矢量保持对象std::condition_variable
获得std::vector<A_with_cv>
A_with_cv
,其中包含不复制成员的复制构造函数和复制赋值运算符std::condition_variable
}。
以下是一个例子:
// compile with: c++ -std=c++14 -o go main.cpp -pthread
#include <iostream>
#include <vector>
#include <condition_variable>
#include <chrono>
#include <mutex>
#include <thread>
class A {
public:
A() {}
A(int d) : data_{d} {}
int data() const { return data_; }
void set_data(int d) { data_ = d; }
private:
int data_{0};
};
class A_with_cv : public A {
public:
A_with_cv(int d = 0) : A{d}
{}
A_with_cv(const A_with_cv &a) : A{static_cast<const A&>(a)} // don't copy cv_ !
{}
A_with_cv &operator=(const A_with_cv &other)
{
static_cast<A&>(*this) = static_cast<const A&>(other); // don't copy cv_ !
}
std::condition_variable &cv() { return cv_; };
const std::condition_variable &cv() const { return cv_; };
private:
std::condition_variable cv_;
};
struct Printer {
public:
Printer(std::vector<A_with_cv> &vec) {
initiate_print(vec);
}
void initiate_print(std::vector<A_with_cv> &vec) {
for (auto it = vec.begin(); it != vec.end(); ) {
auto &val_ref = *it;
++it; // increment
if (it != vec.end()) {
auto &next_ref = *it;
th_vec_.push_back(std::thread(
[&]() {
std::unique_lock<std::mutex> lk(mut_);
val_ref.cv().wait(lk);
std::cout << val_ref.data() << std::endl;
lk.unlock();
next_ref.cv().notify_one(); // notify next
}));
} else {
th_vec_.push_back(std::thread(
[&]() {
std::unique_lock<std::mutex> lk(mut_);
val_ref.cv().wait(lk);
std::cout << val_ref.data() << std::endl;
}));
}
}
}
void wait_for_thread_completion() {
for (auto &th : th_vec_) {
th.join();
}
}
private:
std::mutex mut_;
std::vector<std::thread> th_vec_;
};
int main()
{
std::vector<A_with_cv> vec;
vec.push_back(A_with_cv(1));
vec.push_back(A_with_cv(2));
vec.push_back(A_with_cv(3));
Printer printer(vec);
std::this_thread::sleep_for(std::chrono::milliseconds{500});
vec[0].cv().notify_one();
printer.wait_for_thread_completion();
}
但是在使用矢量时要非常小心。向量可以重新分配元素,您可能认为您正在通知条件变量,而实际上它不再存在(例如,因为您在同一时间内完成了一些push_backs,以及迭代器和向量的引用元素,已失效)!
所以对于这个,更喜欢不重新分配/无效元素的容器,例如std::list
(或 - 如果你只在前面或后面添加元素:std::deque
)等等,因为那样更安全!< / p>
当使用不重新分配/无效元素的此类容器时,请务必使用emplace。例如:
#include <condition_variable>
#include <map>
#include <list>
#include <vector>
#include <deque>
int main()
{
std::deque<std::condition_variable> deq;
//deq.push_back(std::condition_variable()); // does not work
deq.emplace_back();
std::list<std::condition_variable> li;
//li.push_back(std::condition_variable()); // does not work
li.emplace_back();
std::map<int, std::condition_variable> ma;
//ma.insert( std::make_pair(1, std::condition_variable())); // does not work
//ma.emplace(std::make_pair(1, std::condition_variable())); // does not work
ma.emplace(std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple());
return 0;
}
答案 3 :(得分:0)
问题在于,std::vector
在内存中的某个位置分配了一个数组,并且如果该向量后来增长,则需要将该数组移到另一个位置。因此,即使这段代码也无法编译:
std::vector<std::condition_variable> cvvector;
cvvector.emplace_back();
解决方案是使用功能更广泛的容器std::deque
代替std::vector
。 std::deque
永远不会移动为其对象分配的实际存储。尽管如此,std::deque
通过[]
运算符具有恒定的元素引用访问时间:
std::deque<std::condition_variable> cvvector;
cvvector.emplace_back();
cvvector.emplace_back();
std::condition_variable& cv = cvvector[1];
我仍然称容器为cvvector
:只要您不使用cvvector::emplace_front()
或cvvector::pop_front()
,它的行为就象一个向量,而不是一个队列。