gpt4 book ai didi

c++ - 复制具有线程安全规则建议的非const参数的构造函数?

转载 作者:行者123 更新时间:2023-12-01 12:21:41 26 4
gpt4 key购买 nike

我有一些旧代码的包装。

class A{
L* impl_; // the legacy object has to be in the heap, could be also unique_ptr
A(A const&) = delete;
L* duplicate(){L* ret; legacy_duplicate(impl_, &L); return ret;}
... // proper resource management here
};

在此旧版代码中,“复制”对象的函数不是线程安全的(调用相同的第一个参数时),因此在包装器中未将其标记为 const。我猜想遵循现代规则: https://herbsutter.com/2013/01/01/video-you-dont-know-const-and-mutable/

除了细节不是 duplicate之外,此 const看起来是实现复制构造函数的好方法。因此,我不能直接这样做:
class A{
L* impl_; // the legacy object has to be in the heap
A(A const& other) : L{other.duplicate()}{} // error calling a non-const function
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};

那么,解决这种矛盾情况的方法是什么?

(也可以说 legacy_duplicate并不是线程安全的,但是我知道退出时该对象将保持原始状态。作为C函数,该行为仅记录在案,而没有常量性的概念。)

我可以想到许多可能的方案:

(1)一种可能性是,根本没有办法用通常的语义来实现复制构造函数。 (是的,我可以移动对象,而这不是我所需要的。)

(2)另一方面,复制对象本质上是非线程安全的,因为复制简单类型可以找到处于半修改状态的源,因此我可以继续进行此操作,
class A{
L* impl_;
A(A const& other) : L{const_cast<A&>(other).duplicate()}{} // error calling a non-const function
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};

(3)甚至只是声明 duplicate const并在所有上下文中都涉及线程安全。 (毕竟,旧版函数并不关心 const,因此编译器甚至不会提示。)
class A{
L* impl_;
A(A const& other) : L{other.duplicate()}{}
L* duplicate() const{L* ret; legacy_duplicate(impl_, &ret); return ret;}
};

(4)最后,我可以遵循逻辑并制作一个采用 非常量参数的复制构造函数。
class A{
L* impl_;
A(A const&) = delete;
A(A& other) : L{other.duplicate()}{}
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};

事实证明,这在许多情况下都有效,因为这些对象通常不是 const

问题是,这是有效路线还是普通路线?

我不能给它们命名,但是从直觉上我期望在使用非const复制构造函数的过程中会遇到很多问题。由于这种细微差别,它可能不符合值(value)类型的要求。

(5)最后,尽管这似乎是一个过大的选择,并且可能会增加运行时间,但我可以添加一个互斥锁:
class A{
L* impl_;
A(A const& other) : L{other.duplicate_locked()}{}
L* duplicate(){
L* ret; legacy_duplicate(impl_, &ret); return ret;
}
L* duplicate_locked() const{
std::lock_guard<std::mutex> lk(mut);
L* ret; legacy_duplicate(impl_, &ret); return ret;
}
mutable std::mutex mut;
};

但是被迫这样做看起来像是悲观,使类(class)扩大了。我不知道。我目前倾向于 (4)(5)或两者兼而有之。

编辑1:

另外一个选项:

(6)忽略所有重复成员函数的废话,只需从构造函数调用 legacy_duplicate并声明复制构造函数不是线程安全的。 (如果需要,请另外创建一个类型为 A_mt的线程安全versión)
class A{
L* impl_;
A(A const& other){legacy_duplicate(other.impl_, &impl_);}
};

编辑2:

这对于遗留函数的功能而言可能是一个很好的模型。请注意,通过触摸输入,相对于第一个参数表示的值,该调用不是线程安全的。
void legacy_duplicate(L* in, L** out){
*out = new L{};
char tmp = in[0];
in[0] = tmp;
std::memcpy(*out, in, sizeof *in); return;
}

编辑3:
我最近了解到 std::auto_ptr有一个类似的问题,即使用非const“复制”构造函数。结果是 auto_ptr不能在容器内使用。 https://www.quantstart.com/articles/STL-Containers-and-Auto_ptrs-Why-They-Dont-Mix/

最佳答案

我只是同时包含了选项(4)和(5),但是当您认为对性能而言是必要的时,将显式选择加入线程不安全的行为。

这是一个完整的例子。

#include <cstdlib>
#include <thread>

struct L {
int val;
};

void legacy_duplicate(const L* in, L** out) {
*out = new L{};
std::memcpy(*out, in, sizeof *in);
return;
}

class A {
public:
A(L* l) : impl_{l} {}
A(A const& other) : impl_{other.duplicate_locked()} {}

A copy_unsafe_for_multithreading() { return {duplicate()}; }

L* impl_;

L* duplicate() {
printf("in duplicate\n");
L* ret;
legacy_duplicate(impl_, &ret);
return ret;
}
L* duplicate_locked() const {
std::lock_guard<std::mutex> lk(mut);
printf("in duplicate_locked\n");
L* ret;
legacy_duplicate(impl_, &ret);
return ret;
}
mutable std::mutex mut;
};

int main() {
A a(new L{1});
const A b(new L{2});

A c = a;
A d = b;

A e = a.copy_unsafe_for_multithreading();
A f = const_cast<A&>(b).copy_unsafe_for_multithreading();

printf("\npointers:\na=%p\nb=%p\nc=%p\nc=%p\nd=%p\nf=%p\n\n", a.impl_,
b.impl_, c.impl_, d.impl_, e.impl_, f.impl_);

printf("vals:\na=%d\nb=%d\nc=%d\nc=%d\nd=%d\nf=%d\n", a.impl_->val,
b.impl_->val, c.impl_->val, d.impl_->val, e.impl_->val, f.impl_->val);
}

输出:
in duplicate_locked
in duplicate_locked
in duplicate
in duplicate

pointers:
a=0x7f85e8c01840
b=0x7f85e8c01850
c=0x7f85e8c01860
c=0x7f85e8c01870
d=0x7f85e8c01880
f=0x7f85e8c01890

vals:
a=1
b=2
c=1
c=2
d=1
f=2

这遵循 Google style guide,其中 const传达线程安全性,但是调用API的代码可以使用 const_cast退出

关于c++ - 复制具有线程安全规则建议的非const参数的构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60087792/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com