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
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