Eski bir kod parçası için bir sarıcı var.
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
};
Bu eski kodda, bir nesneyi “çoğaltan” işlev iş parçacığı için güvenli değildir (aynı ilk bağımsız değişken çağrılırken), bu nedenle const
sarmalayıcıda işaretlenmez . Modern kurallara uyduğumu tahmin ediyorum: https://herbsutter.com/2013/01/01/video-you-dont-know-const-and-mutable/
Bu duplicate
, olmayan bir ayrıntı dışında bir kopya oluşturucu uygulamak için iyi bir yol gibi görünüyor const
. Bu yüzden bunu doğrudan yapamam:
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;}
};
Peki bu paradoksal durumun çıkış yolu nedir?
(Diyelim ki bu legacy_duplicate
iş parçacığı için güvenli değil ama çıktığı zaman nesneyi orijinal durumda bırakıyor. C işlevi olarak, davranış sadece belgelenmiştir, ancak sabitlik kavramı yoktur.)
Birçok olası senaryo düşünebilirim:
(1) Bir olasılık, olağan anlambilim ile bir kopya oluşturucu uygulamanın hiçbir yolu olmamasıdır. (Evet, nesneyi hareket ettirebilirim ve ihtiyacım olan şey bu değil.)
(2) Öte yandan, bir nesneyi kopyalamak, basit bir tür kopyalamanın kaynağı yarı değiştirilmiş durumda bulabilmesi açısından doğal olarak iş parçacığı açısından güvenli değildir, bu yüzden sadece ileri gidip bunu yapabilirim,
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) veya hatta duplicate
tüm bağlamlarda sabitlik ve iplik güvenliği hakkında yalan söylemek. (Sonuçta eski işlev umurunda değil, const
bu yüzden derleyici şikayet bile etmeyecektir.)
class A{
L* impl_;
A(A const& other) : L{other.duplicate()}{}
L* duplicate() const{L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
(4) Son olarak, mantığı takip edebilir ve sabit olmayan bir argüman alan bir kopya oluşturucu yapabilirim .
class A{
L* impl_;
A(A const&) = delete;
A(A& other) : L{other.duplicate()}{}
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
Bunun birçok bağlamda işe yaradığı ortaya çıkıyor, çünkü bu nesneler genellikle değil const
.
Soru şu, bu geçerli veya ortak bir rota mı?
Onları adlandıramam, ancak sezgisel olarak const olmayan bir kopya oluşturucuya sahip olmanın yolunda birçok sorun bekliyorum. Muhtemelen bu incelik nedeniyle bir değer türü olarak nitelendirilmeyecektir.
(5) Son olarak, bu bir overkill gibi görünüyor ve dik bir çalışma zamanı maliyeti olabilir, ancak bir muteks ekleyebilirim:
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;
};
Ama bunu yapmaya zorlanmak, kötümserlik gibi görünüyor ve sınıfı büyütüyor. Emin değilim. Şu anda (4) veya (5) 'e ya da her ikisinin bir kombinasyonuna yaslanıyorum .
DÜZENLEME 1:
Başka seçenek:
(6) Yinelenen üye işlevinin tüm anlamsızlıklarını unutun ve yalnızca legacy_duplicate
yapıcıdan arayın ve kopya yapıcısının iş parçacığı için güvenli olmadığını bildirin. (Gerekirse, tipte başka bir iş parçacığı için güvenli bir sürüm yapın A_mt
)
class A{
L* impl_;
A(A const& other){legacy_duplicate(other.impl_, &impl_);}
};
DÜZENLEME 2:
Bu, eski fonksiyonun yaptığı şey için iyi bir model olabilir. Girişe dokunulduğunda ilk argümanın temsil ettiği değere göre çağrının güvenli olmadığını unutmayın.
void legacy_duplicate(L* in, L** out){
*out = new L{};
char tmp = in[0];
in[0] = tmp;
std::memcpy(*out, in, sizeof *in); return;
}
DÜZENLEME 3:
Son zamanlarda, std::auto_ptr
sabit olmayan bir "kopya" kurucuya sahip olmak gibi benzer bir sorunu olduğunu öğrendim . Sonuç, auto_ptr
bir kabın içinde kullanılamamasıydı. https://www.quantstart.com/articles/STL-Containers-and-Auto_ptrs-Why-They-Dont-Mix/
legacy_duplicate
iki farklı evreden aynı ilk argümanla çağrılamaz.
const
gerçekten ne anlama geldiğini bilmeyen insanlardan biriyim . :-) Ben const&
değiştirmedikçe benim kopya ctor bir almak hakkında iki kez düşünmek olmaz other
. Ben iplik güvenliği her zaman bir kapsülleme yoluyla, birden çok iş parçacığı erişilmesi gereken her şeyin üzerine eklediği bir şey olarak düşünüyorum ve gerçekten cevapları dört gözle bekliyorum.
L
yeni birL
örnek oluşturularak değiştirilen bir durum var mı? Değilse, neden bu işlemin iş parçacığı için güvenli olmadığını düşünüyorsunuz?