2014-09-24 27 views
5

Eğer değer türleri ile C++ std :: harita (ve diğer kapları) kullanıyorsanız, harita içine yerleştirirken sizin öğe türü için yıkıcı çağırır göreceksiniz. O çiftini oluşturmak için türünüze varsayılan kurucuyu çağıranNeden C++ std :: map :: operator [] inplace yeni kullanıyor?

(*((std::map<>::insert(std::make_pair(x, T()))).first)).second 

: operatör uygulanması [] Buna eşdeğer olmak C++ Spec gerektirdiği olmasıdır. Bu geçici değer daha sonra haritaya kopyalanır ve sonra imha edilir. Bu teyidi this stackoverflow post ve here on codeguru bulunabilir.

Ne garip buluyorum bu eşdeğer hala geçici değişken için gerek kalmadan uygulanabilir ve olabilir olmasıdır. C++ özelliği "inplace new" olarak adlandırılmıştır. Std :: map ve diğer kapsayıcılar, nesnenin yaşaması için boş alan tahsis edebilir ve daha sonra ayrılan alanda öğenin varsayılan yapıcısını açıkça çağırır.

Soruma Soru: Neden std :: map uygulamalarının hiçbiri, bu işlemi en iyi duruma getirmek için inplace yeni kullanımının hiçbiri yapmıyor? Bana öyle geliyor ki, bu düşük seviyedeki operasyonun performansını önemli ölçüde artıracaktır. Ancak pek çok göz STL kod tabanını çalışmış, bu yüzden bu şekilde yapılmasının bir sebebi olmalı diye düşünüyorum.

+0

Test durumu, bağlantılı stackoverflow gönderisinde bulunur. İşte yine bağlantı: https://stackoverflow.com/questions/4017892/in-an-stl-map-of-structs-why-does-the-operator-cause-the-structs-dtor-to – srm

+0

Mike Seymour: Öyleyse hata ayıklayıcısını kapattığında, yok ediciye yapılan bu ekstra çağrılar kaçar mı? Bunun doğru olduğunu biliyor musun? Bu doğru olsa bile, hata ayıklama ile, yürütmenin hata ayıklaması daha zor olduğu anlamına gelir (sadece "gerçek" kapsam sorunları arayan hata ayıklayıcımda bir kesme noktası ayarlayamıyorum). Daha az sorun olan, ancak yine de gerçek olan hata ayıklama sırasında daha az performans gösterir. Bu nedenlerin ikisi de, bu tür düşük seviyeli bir kütüphane için yerine yeni olanları kullanmak için nedenler olarak görünebilir. – srm

cevap

4

Genel olarak, alt düzey olanlar açısından [] gibi yüksek bir seviyede işlemini belirten iyi bir fikirdir.

C++ 11'den önce [] ile bunu yapmak insert kullanmadan zor olabilir. C

11 ++, std::pair için std::map<?>::emplace eklenmesi ve benzeri şeyler bize bu sorunu önlemek olanağı verir. Eğer bunu yeniden tanımladıysanız, böyle bir yerinde yapı kullanacak olursanız, ekstra (umaruzca elenmiş) nesne yaratır.

ben bunu bir sebep düşünemiyorum. Standardizasyon için teklif etmenizi öneririm.

biz aşağıdakileri yapabilirsiniz, bir std::map içine kopyalamaya karşı daha az ekleme göstermek için:

#include <map> 
#include <iostream> 

struct no_copy_type { 
    no_copy_type(no_copy_type const&)=delete; 
    no_copy_type(double) {} 
    ~no_copy_type() { std::cout << "destroyed\n"; } 
}; 
int main() { 
    std::map< int, no_copy_type > m; 
    m.emplace(
    std::piecewise_construct, 
    std::forward_as_tuple(1), 
    std::forward_as_tuple(3.14) 
); 
    std::cout << "destroy happens next:\n"; 
} 

live example - Gördüğünüz gibi, hiçbir geçici oluşturulur.

yüzden

(* 
    (
    (
     std::map<>::emplace(
     std::piecewise_construct, 
     std::forward_as_tuple(std::forward<X>(x)), 
     std::forward_as_tuple() 
    ) 
).first 
).second 

ile

(*((std::map<>::insert(std::make_pair(x, T()))).first)).second 

değiştirirseniz hiçbir geçici yaratılmış olacaktır (boşluk ı () s takip böylece eklendi).

+0

Mike Seymour'un daha önceki cevabı, programcıların alternatif bir Allocator seçebilme yeteneğinin, geçici olarak inşaatın koddan kaldırılamayacağı ve optimizasyon sırasında, hata ayıklama bakış açısından hem de bu durumun acıklı olduğu anlamına geldiği anlamına gelir. Yeni kullanıcılar ekleme sırasında bir yıkıcı neden çağırılır. – srm

+0

@srm Sure. Ama Mike yanlış. Standartta küçük bir kusur (verimsizlik), zaten sabit olmadığı varsayılarak (en son kontrol edilmemişti). Allocators rebound olabilir ve imparatorluk ve parçalı inşaat, herhangi bir geçici olmadan nesneyi inşa etmenize izin verebilir. Varsayılan yapı boş bir “tuple” ile yapılabilir. – Yakk

+0

Yorum yapmak için yeterli bilgim yok. Bu yüzden soruyu sordum. Yorumunuzu Mike'ın cevabına gönderebilir misiniz? Böylece bildirim alır ve ikinizin birbirini ikna edip edemeyeceğini görürsünüz? – srm

0

Öncelikle, std::map için operator[<key>] talep <key> bulunamazsa eğer bir ekleme işlemi sadece eşdeğerdir. Bu durumda, sadece bir referansa ihtiyaç duyulur ve üretilen depolanmış değere sadece bir referans gerekir.

İkincisi, yeni öğe eklendiğinde, bir kopyalama işleminin yapılıp yapılmayacağını bilmenin bir yolu yoktur. Sen map[_k] = _v; sahip olabilir veya _v = map[_k]; olabilir.Sonuncusu, bir ödevin dışında, yani map[_k].method_call();'da olduğu gibi aynı gerekliliklere sahiptir, fakat 10 ()'un kopya yapıcısını kullanmamaktadır (bu, yapılandırılacak bir kaynak değildir). Ekleme ile ilgili olarak, yukarıdakilerin tümü, value_type varsayılan kurucusunun çağrılmasını ve bu alanın bunun için tahsis edilmesini gerektirir. Atama kullanımında olduğumuz için operator[] yazarken bile bilsek bile, operasyon sırasından dolayı “inplace new” i kullanamadık. İlk olarak value_type yapıcısının çağrılması gerekiyordu, ardından kopya kurucusunun çağrılmasını gerektiren value_type::operator=. Yine de düşünmek güzeldir.

İlgili konular