C++23: Aralık ve std::generator iyileştirmeleri

Adanali

Active member
C++23: Aralık ve std::generator iyileştirmeleri


  1. C++23: Aralık ve std::generator iyileştirmeleri

C++20 somut eşyordamlar sağlamaz ancak bunların uygulanması için bir çerçeve sağlar. Bu C++23 ile değişir. std::generator ilk somut koroutindir.

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.













std::generator C++23’teki Ranges kitaplık uzantısının bir parçasıdır. Bu nedenle bu yazıya C++20’deki Ranges kütüphanesi ve C++23’teki uzantısı ile başlamak istiyorum. Ama kısa keseceğim. C++20’deki Ranges kütüphanesi ve C++23’teki uzantısı hakkında zaten yazmıştım:

Tamamlamak istediğim tek bir hikaye var:

Pitonlar range C++23’teki işlev


“Pythonik olarak aralık kitaplığıyla: aralık ve filtre” ve “Python’un aralık işlevi, ikincisi” adlı makalelerimde range-Python 2’de Ranges kütüphanesi kullanılarak uygulanan işlev. Python 2 ve Python 3 arasındaki fark range-İşlev, sürüm 2’nin istekli, ancak sürüm 3’ün tembel olmasıdır. Bu, sürüm 2’nin sayıları ürettiği, sürüm 3’ün ise isteğe bağlı olarak sayıları üreten bir oluşturucu döndürdüğü anlamına gelir. Sorunu yalnızca C++20’de Eric Niebler’in range-v3 kitaplığını kullanarak çözebildim. Özellikle bu özelliğe ihtiyacım vardı stride(N)başka bir Görünüme kayar ve bunu her seferinde yapar Nöğe geri döner.

std::ranges::views::stride C++23’ün bir parçasıdır ve GCC ve MSVC derleyicileri onu destekler. Ayrıca bu örnekte pratik fonksiyonunu kullanıyorum std::ranges::to. Bu C++23 işlevi aralıklardan kapsayıcılar ve dizeler oluşturmak için kullanılabilir. Bu özellik şu anda yalnızca MSVC ve Clang derleyicileri tarafından desteklenmektedir.

Şimdi işte Python olanı range-C++’daki işlev:


// rangeCpp23.cpp

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

std::vector<int> range(int begin, int end, int stepsize = 1) {
std::vector<int> result{};
if (begin < end) { // (5)
auto boundary = [end](int i){ return i < end; };
result = std::ranges::views::iota(begin)
| std::views::stride(stepsize)
| std::views::take_while(boundary)
| std::ranges::to<std::vector>();
}
else { // (6)
begin++;
end++;
stepsize *= -1;
auto boundary = [begin](int i){ return i < begin; };
result = std::ranges::views::iota(end)
| std::views::take_while(boundary)
| std::views::reverse
| std::views::stride(stepsize)
| std::ranges::to<std::vector>();
}
return result;
}

int main() {

std::cout << std::endl;

// range(1, 50) // (1)
auto res = range(1, 50);
for (auto i: res) std::cout << i << " ";

std::cout << "nn";

// range(1, 50, 5) // (2)
res = range(1, 50, 5);
for (auto i: res) std::cout << i << " ";

std::cout << "nn";

// range(50, 10, -1) // (3)
res = range(50, 10, -1);
for (auto i: res) std::cout << i << " ";

std::cout << "nn";

// range(50, 10, -5) // (4)
res = range(50, 10, -5);
for (auto i: res) std::cout << i << " ";

std::cout << "nn";

}


(1) – (4) arasındaki çağrılar, MSVC derleyici çıktısına bakarak okunabilecek kadar kolay olmalıdır:








Aralık çağrısının ilk iki bağımsız değişkeni, oluşturulan sayıların başlangıcını ve bitişini temsil eder. Başlangıç dahildir, ancak son dahil değildir. Varsayılan olarak üçüncü parametre olarak adım boyutu 1’dir. [begin, end] düşerse adım boyutu negatif olmalıdır. Aksi takdirde boş veya boş bir listeyle karşılaşırsınız std::vector<int>.

Koşul eğer (begin < end) ranges-(1)’deki fonksiyonun okunması yeterince kolay olmalıdır: sağlanan tüm sayıları oluşturur begin başlangıç (std::views::iota(begin)), her n’inci elemanı al (std::views::stride(stepsize)) ve sınır koşulu geçerli olduğu sürece bunu yapın (std::views::take_while(boundary)). Sonunda onu ben yaratıyorum std::vector<int>. İçinde else-Durum (2) Küçük bir numara kullanıyorum: Sayıları yaratıyorum [end++, begin++[, nehme sie, bis die Randbedingung erfüllt ist, drehe sie um (std::views::reverse) und nehme jedes n-te Element.

Nun möchte ich mich den Koroutinen widmen.

std::generator


std::generator in C++23 ist die erste konkrete Koroutine. Ein std::generator erzeugt eine Folge von Elementen, in dem die Koroutine dort ihren Kontrollfluss wieder aufnimmt, wo sie pausiert hat.


// generator.cpp

#include <generator>
#include <ranges>
#include <iostream>

std::generator<int> fib() {
co_yield 0; // (1)
auto a = 0;
auto b = 1;
for(auto n : std::views::iota(0)) {
auto next = a + b;
a = b;
b = next;
co_yield next; // (2)
}
}

int main() {
for (auto f : fib() | std::views::take(10)) {
std::cout << f << " ";
}
}


Die Funktion fib ist eine Koroutine. Diese Koroutine erzeugt einen unendlichen Strom von Fibonacci-Zahlen. Der Zahlenstrom beginnt mit 0 (1) und wird mit der nächsten Fibonacci-Zahl fortgesetzt (2). Die Range-based for-Schleife fordert explizit die ersten 10 Fibonacci-Zahlen an.

Bislang unterstützt kein Compiler std::generator. Es lässt sich die Koroutinen-Natur von std::generator schön an seinem Header studieren:

std::ranges::elements_of kommt ins Spiel, wenn ein Generator rekursiv aufgerufen werden soll.


std::generator<int> fib() {
co_yield 0;
auto a = 0;
auto b = 1;
for(auto n : std::views::iota(0)) {
auto next = a + b;
a = b;
b = next;
co_yield next;
}
}

std::generator<int> outer() {
yield fib(); // (1)
yield std::ranges::elements_of(fib); // (2)
}


Der äußere Generator gibt in Zeile (1) den inneren std::generator<int> zurück, aber in Zeile (2) die Werte des inneren Generators. Beide Koroutinen besitzen den gleichen Rückgabetyp.

std::bind_back


Entsprechend zu std::bind_front in C++20 unterstützt C++23 std::bind_back. Das folgende Programm bindFrontBack.cpp zeigt die Anwendung der beiden Funktionen.


// bindFrontBack.cpp

#include <functional>
#include <iostream>
#include <string>

int main() {

std::cout << 'n';

auto add = [](std::string a, std::string b, std::string c) { return a + b + c; }; araba iki_üç = std::bind_front(ekle, "bir"); std::cout code>


Çıktı sayesinde program kendini açıklamalıdır.








Kısmi fonksiyon uygulamaları hakkında daha fazla bilgi edinin. std::bind, std::bind_front VE std::bind_back "Yazılım Geliştirme Teknikleri: Kısmi Fonksiyonların Uygulanması" adlı makalemde bulabilirsiniz.

Sıradaki ne?


Artık C++23 ile işim bitti. Bu yüzden altı yıl geriye gitmek istiyorum. Bir sonraki yazımda C++17'de neredeyse hiç bilinmeyen bir özellikten bahsedeceğim: polimorfik ayırıcılar.


(kendim)



Haberin Sonu
 
Üst