C++20’deki Ranges Kitaplığı: Diğer Tasarım Kararları

Adanali

Active member
C++20’deki Ranges Kitaplığı: Diğer Tasarım Kararları


  1. C++20’deki Ranges Kitaplığı: Diğer Tasarım Kararları

Performans açısından C++20’deki Ranges kütüphanesinin bazı özel özellikleri vardır. Bu tasarım kararlarının sonuçları vardır: önbellek ve tutarlılık sorunları.

Duyuru








Rainer Grimm uzun yıllardır yazılım mimarı, ekip ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanıyor, aynı zamanda özel konferanslarda sık sık konuşmaktan da hoşlanıyor. Modern C++ adlı blogunda C++ tutkusunu yoğun bir şekilde ele alıyor.













Burada kısa bir hatırlatma yapalım. Son makalem olan “C++20’deki Ranges Kütüphanesi: Tasarım Kararları”nda bu olası uygulamayı tartıştım. std::ranges::filter_view sundu:


if constexpr (!ranges::forward_range<V>)
return /* iterator */{*this, ranges::find_if(base_, std::ref(*pred_))};
else
{
if (!begin_.has_value())
begin_ = ranges::find_if(base_, std::ref(*pred_)); // caching
return /* iterator */{*this, begin_.value())};
}


En önemli gözlem şu ki begin-Yineleyici sonraki çağrılar için önbelleğe alınır. Bu önbelleğe almanın iki ilginç sonucu vardır:

  • Görünümü değiştirilen alanlarda kullanamazsınız ve
  • bir görünümü kopyalamanıza izin verilmiyor.
Veya olumlu bir şekilde şöyle dedi: Görünümleri tanımladıktan sonra doğrudan kullanmanız gerekir.

Daha da önemli tasarım kararları var.

Konstanz


Bir görünümün üye işlevi konumu önbelleğe alabilir. Bu, aşağıdaki noktaları ifade eder:

  • Keyfi bir görüşü kabul eden bir işlev, onu evrensel referansla kabul etmelidir.
  • İki görselleştirmenin aynı anda okunması veri yoğunluğuna yol açabilir.
Öncelikle ilk noktaya değinmek istiyorum.

Evrensel bir referans kullanarak herhangi bir bakış açısını değerlendirin


İşlev printElements vizyonunuzu evrensel referansla kabul edin.


void printElements(std::ranges::input_range auto&& rang) {
for (int i: rang) {
std::cout << i << " ";
}
std::cout << 'n';
}


printElements iddialarını evrensel referansla kabul eder. Bunları referans değeri olarak veya değer olarak kabul etmek olumsuz sonuçlar doğurur.

Argümanı sabit değere referansla kabul etmek başarısız olur çünkü örtülü begin– Görünümü çağırmak konuyu değiştirebilir. Bunun tersine, const olmayan bir değer referansı bir değer değerini işleyemez.

Ancak bağımsız değişkeni bir değer olarak kabul etmek önbelleği geçersiz kılabilir.

Görselleştirmeleri aynı anda okumak veri akışına neden olabilir


Aşağıdaki programda ekran eşzamanlılığı sorunu gösterilmektedir:


// dataRaceRanges.cpp

#include <numeric>
#include <iostream>
#include <ranges>
#include <thread>
#include <vector>

int main() {

std::vector<int> vec(1'000);
std::iota(vec.begin(), vec.end(), 0);

auto first5Vector = vec | std::views::filter([](auto v) { return v > 0; })
| std::views::take(5);

std::jthread thr1([&first5Vector]{
for (int i: first5Vector) {
std::cout << i << " ";
}
});


for (int i: first5Vector) {
std::cout << i << " ";
}

std::cout << "nn";

}


Program boyunca yineleyin dataRaceRanges.cpp bir görünümü değiştirmeden aynı anda iki kez. İlk önce tekrar ediyorum std::jthread thr1 ve sonra main-İşlev. Bu bir veri yarışıdır çünkü her iki yineleme de örtülü olarak üyelik işlevini kullanır begin konumu önbelleğe alabilecek bir kullanım. ThreadSanitizer bu veri akışını açığa çıkarıyor ve 24. satıra daha önce yazılan bir yazıdan şikayet ediyor: std::cout << i << " ";








Bunun tersine, klasik bir kapsayıcıda gezinmek şuna benzer: std::vector tel için güvenli. Klasik kapsayıcılar ile görünümler arasında başka bir fark daha vardır.

Sabitliğin yayılması


Klasik kaplar derin bir istikrar modelidir. Bunları kendi unsurlarına aktarırlar. Bu, sabit bir kabın elemanlarını değiştirmenin imkansız olduğu anlamına gelir.


// constPropagationContainer.cpp

#include <iostream>
#include <vector>

template <typename T>
void modifyConstRange(const T& cont) {
cont[0] = 5;
}

int main() {

std::vector myVec{1, 2, 3, 4, 5};
modifyConstRange(myVec); // ERROR

}


Arama modifyConstRange(myVec)derleme zamanında hataya neden olur.

Buna karşılık, görünümler düz sabitliği modelliyor. Bunları kendi unsurlarına aktarmazlar. Bu nedenle öğeler yine de düzenlenebilir.


// constPropagationViews.cpp

#include <iostream>
#include <ranges>
#include <vector>

template <typename T>
void modifyConstRange(const T& cont) {
cont[0] = 5;
}

int main() {

std::vector myVec{1, 2, 3, 4, 5};

modifyConstRange(std::views::all(myVec)); // OK

}


Arama modifyConstRange(std::views::all(myVec)) Elbette.

Sıradaki ne?


Eşyordamlar muhtemelen C++20’nin en zorlu bileşenidir. Bir sonraki makalem Dian-Lun Lin’in misafir yazısıdır. Eşyordamlara kısa bir giriş yapacak ve görevleri yöneten basit bir zamanlayıcı kullanarak fikrini açıklayacak.


(kendim)



Haberin Sonu
 
Üst