C++ProgramlamaC++ Geliştirici

**std::pmr::vector<std::string>**'in dahili string saklama için **std::pmr::polymorphic_allocator**'ı kullanmasını engelleyen tür uyumsuzluğunu tanımlayın.

Hintsage yapay zeka asistanı ile mülakatları geçin

Sorunun cevabı

Uyumsuzluk, std::uses_allocator tür özelliğinden kaynaklanmaktadır; bu özellik std::string ve std::pmr::polymorphic_allocator kombinasyonu için false değerini değerlendirir. std::string, allocator_type'ını std::allocator<char> olarak katıştırırken, std::pmr::vector std::pmr::polymorphic_allocator<char> sağlar; bunlar, örtük dönüşüm veya miras ilişkisi olmayan ayrı, ilgisiz sınıf türleridir. Konteyner elemanları oluşturduğunda, tahsisçinin bir kurucu argümanı olarak geçirilip geçirilmeyeceğini belirlemek için std::uses_allocator_v<T, Alloc>'i sorgular; bu kontrol başarısız olduğu için, vektör std::string'i tahsisçi-farkında olarak kabul eder ve varsayılan kurucusunu çağırır; bu da, vektörün bellek kaynağından bağımsız olarak global new ve delete kullanır.

static_assert(!std::uses_allocator_v<std::string, std::pmr::polymorphic_allocator<char>>); // std::pmr::vector, tahsisçisini std::string'e geçmeyecek

Gerçek hayattan bir durum

Finansal risk hesaplama motorunun optimizasyonu sırasında, yığından desteklenen std::pmr::monotonic_buffer_resource kullanmak için bir sıcak yolu yeniden düzenledik ve yığın çakışmasını ortadan kaldırdık. Tüm geçici sembol adlarının monotonik bellekten sağlanmasını bekleyerek std::pmr::vectorstd::string temp_symbols olarak tanımladık, ancak performans profilinde std::string kurucuları içerisinde beklenmedik malloc çağrıları tespit edildi ve bellek kaynağının tamamen atlandığı görüldü.

Her bir std::string'i, kurucusuna açık bir std::pmr::polymorphic_allocator geçirerek elle oluşturmaya çalıştık, ancak bu, tahsis detaylarını daha yüksek seviyedeki iş mantığına ifşa etmeyi gerektiriyordu ve emplace_back gibi kullanışlı modifikatörlerin kullanılmasını engelliyordu. Diğer bir yaklaşım, std::string'den türeyen ve polimorfik bir tahsisçi kabul eden özel bir string sarmalayıcı oluşturmayı içeriyordu; ancak bu, Liskov ikame ilkesini ihlal ediyor ve konteyner yeniden tahsis edilirken nesne dilimleme risklerini artırıyordu. Nihayetinde, std::string'i std::pmr::string ile değiştirdik (std::basic_string<char, std::char_traits<char>, std::pmr::polymorphic_allocator<char> için bir takma ad), bu da allocator_type'ı polimorfik varyant olarak tanımlar. Bu, vektörün tahsisçisini otomatik olarak uses_allocator protokolü aracılığıyla iletmesine olanak tanıdı, sıcak yol üzerindeki tüm yığın tahsislerini ortadan kaldırdı ve gecikmeyi mikrosaniyelerden yüzlerce nanosaniyeye düşürdü.

Adayların sıklıkla gözden kaçırdığı şeyler

İçsel dinamik tahsis gerçekleştiren bir özel sınıf nasıl std::pmr::polymorphic_allocator ile uyumlu hale getirilebilir; yalnızca inşaatçısında bir tahsisçi parametresi kabul etmek yetersizdir?

Bir sınıf, ya kamuya açık bir allocator_type tür takma adı sunarak ya da ilk parametresi std::allocator_arg_t ve ikinci parametresi tahsisçi türü olan bir kurucu sağlayarak tahsisçi-farkındalığını açıkça bildirmelidir. Bununla birlikte, std::uses_allocator<ClassName, Alloc>'i std::true_type üzerinden türetmek de gerekir. Bu açık reklâm olmadan, std::pmr::vector sınıfın tahsisçi-farkında olduğunu varsayar ve varsayılan başlangıç ile oluşturur; bu, herhangi bir iç tahsisin polimorfik bellek kaynağını atlamasına neden olur.

Neden std::allocator_traits<std::pmr::polymorphic_allocator<T>>::rebind_alloc<U>, std::pmr::vector ile std::string arasındaki uyumsuzluğu çözmez?

Yeniden bağlama, std::pmr::polymorphic_allocator<U> üretir; bu, std::allocator<U> ile uyumsuzdur çünkü bunlar örtük dönüşüm ilişkisi olmayan ayrı somut türlerdir. std::uses_allocator mekanizması, elemanın allocator_type'ının, konteynerin tahsisçi türüyle aynı olması veya ondan dönüştürülebilmesi gerektiğini gerektirir; sadece farklı bir değer türüne yeniden bağlanabilir olması yeterli değildir; çünkü std::string, std::allocator'ı katıştırır, konteynerin tahsisçisinin yeniden bağlanması, elemanın beklenen tahsisçi türünü değiştirmez.

std::pmr::monotonic_buffer_resource ile std::pmr::string kullanırken hangi belirli yaşam süresi riski doğar ve neden bu tespiti standart tahsisçilerden daha zor hale getirir?**

Çünkü std::pmr::polymorphic_allocator tür silinmiş durumdadır ve bir temel std::pmr::memory_resource'a işaret eden bir gösterici saklar, derleyici yaşam süresi kısıtlamalarını derleme zamanında uygulayamaz. Yığından desteklenen bir monotonic_buffer_resource'u referans alan bir std::pmr::string taşındığında veya kopyalandığında, bellek kaynağına olan gösterici asılı hale gelir; std::allocator genellikle global yığın (her zaman geçerli) kullandığı için, tampon imhadan sonra string'e erişmek, serbest bırakıldığını kullanmaya neden olur. Statik analizörler bunu tespit etmekte zorluk çekerler çünkü sanal do_allocate/do_deallocate arayüzü, tür sisteminden altta yatan kaynağın yaşam süresini gizler.