移动具有非默认可构造保护的构造函数 class
Move constructor with protection for non-default-constructible class
让我们假设我有一个非默认构造的 class 像这样:
class A {
public:
int k;
A() = delete;
A(int _k): k{_k}{};
A(A const& o) = delete;
A& operator=(A const& o) = delete;
A(A&& o) = default;
A& operator=(A&& o) = default;
};
然后,我有一个简单的互斥量:
class Mutex {
public:
void take();
void give();
};
现在,我有一个组合 class,我想保护 A
class(和其他成员)上的每个操作,包括移动构造它们:
class C {
A a;
A b;
Mutex m;
C() = delete;
C(int _k, int _l) : m{}, a{_k}, b{_l} {}
C(C&& other) : m{} { // PROBLEM HERE : use of deleted constructor
other.m.take(); // <-- this disallows the use of initializer list
a{std::move(other.a)};
b{std::move(other.b)};
other.m.give();
}
};
这会引发错误,因为它会在进入构造函数主体之前尝试默认构造 a
成员。 有没有办法用互斥锁保护 a
的移动构造?
您可以在辅助函数中进行锁定:
class C {
A a;
std::mutex m; // using a standard mutex instead
A A_mover(C&& other) {
std::lock_guard<std::mutex> lock(other.m);
return std::move(other.a); // move into a temporary while locked
}
public:
C() = delete;
C(int _k) : a{_k}, m{} {}
C(C&& other) : a(A_mover(std::move(other))), m{} {}
};
如果 C
本身由多个字段组成,请将互斥量移出包装器 class。理想情况下,包装器应该只保留一个对象 + 一个互斥体。这使用了您的 Mutex
,因为标准 std::mutex
似乎不可用。
class C {
A a;
A b;
public:
C() = delete;
C(int _k, int _l) : a{_k}, b{_l} {}
C(C&& other) = default;
};
class CLocker {
public:
template<typename...Args>
CLocker(Args...args) : c(std::forward<Args>(args)...) {}
CLocker(CLocker&& other) : c(mover(std::move(other))) {}
private:
struct MutexLockGuard {
MutexLockGuard(Mutex& M) : m(M) { m.take(); }
~MutexLockGuard() { m.give(); }
Mutex& m;
};
C mover(CLocker&& other) {
MutexLockGuard lock(m);
return std::move(other.c); // move into a temporary while locked
}
C c;
Mutex m;
};
int main() {
CLocker cl1(10, 20);
CLocker cl2(std::move(cl1));
}
@Jarod42 建议最后一个没有包装器的选项:
class MutexLockGuard {
public:
MutexLockGuard(Mutex& M) : m(M) { m.take(); }
~MutexLockGuard() { m.give(); }
private:
Mutex& m;
};
class C {
public:
C() = delete;
C(int _k, int _l) : a{_k}, b{_l}, m{} {}
C(C&& other) : C(MutexLockGuard(other.m), std::move(other)) {}
//^ delegate to protected constructor
protected:
C(MutexLockGuard, C&& other) : // do the moves while the lock is held
a{std::move(other.a)},
b{std::move(other.b)},
m{}
{}
private:
A a;
A b;
Mutex m;
};
让我们假设我有一个非默认构造的 class 像这样:
class A {
public:
int k;
A() = delete;
A(int _k): k{_k}{};
A(A const& o) = delete;
A& operator=(A const& o) = delete;
A(A&& o) = default;
A& operator=(A&& o) = default;
};
然后,我有一个简单的互斥量:
class Mutex {
public:
void take();
void give();
};
现在,我有一个组合 class,我想保护 A
class(和其他成员)上的每个操作,包括移动构造它们:
class C {
A a;
A b;
Mutex m;
C() = delete;
C(int _k, int _l) : m{}, a{_k}, b{_l} {}
C(C&& other) : m{} { // PROBLEM HERE : use of deleted constructor
other.m.take(); // <-- this disallows the use of initializer list
a{std::move(other.a)};
b{std::move(other.b)};
other.m.give();
}
};
这会引发错误,因为它会在进入构造函数主体之前尝试默认构造 a
成员。 有没有办法用互斥锁保护 a
的移动构造?
您可以在辅助函数中进行锁定:
class C {
A a;
std::mutex m; // using a standard mutex instead
A A_mover(C&& other) {
std::lock_guard<std::mutex> lock(other.m);
return std::move(other.a); // move into a temporary while locked
}
public:
C() = delete;
C(int _k) : a{_k}, m{} {}
C(C&& other) : a(A_mover(std::move(other))), m{} {}
};
如果 C
本身由多个字段组成,请将互斥量移出包装器 class。理想情况下,包装器应该只保留一个对象 + 一个互斥体。这使用了您的 Mutex
,因为标准 std::mutex
似乎不可用。
class C {
A a;
A b;
public:
C() = delete;
C(int _k, int _l) : a{_k}, b{_l} {}
C(C&& other) = default;
};
class CLocker {
public:
template<typename...Args>
CLocker(Args...args) : c(std::forward<Args>(args)...) {}
CLocker(CLocker&& other) : c(mover(std::move(other))) {}
private:
struct MutexLockGuard {
MutexLockGuard(Mutex& M) : m(M) { m.take(); }
~MutexLockGuard() { m.give(); }
Mutex& m;
};
C mover(CLocker&& other) {
MutexLockGuard lock(m);
return std::move(other.c); // move into a temporary while locked
}
C c;
Mutex m;
};
int main() {
CLocker cl1(10, 20);
CLocker cl2(std::move(cl1));
}
@Jarod42 建议最后一个没有包装器的选项:
class MutexLockGuard {
public:
MutexLockGuard(Mutex& M) : m(M) { m.take(); }
~MutexLockGuard() { m.give(); }
private:
Mutex& m;
};
class C {
public:
C() = delete;
C(int _k, int _l) : a{_k}, b{_l}, m{} {}
C(C&& other) : C(MutexLockGuard(other.m), std::move(other)) {}
//^ delegate to protected constructor
protected:
C(MutexLockGuard, C&& other) : // do the moves while the lock is held
a{std::move(other.a)},
b{std::move(other.b)},
m{}
{}
private:
A a;
A b;
Mutex m;
};