C++23: bunun çıkarıldığı sözdizimsel şeker

Adanali

Active member
C++23: bunun çıkarıldığı sözdizimsel şeker


  1. C++23: bunun çıkarıldığı sözdizimsel şeker

ONLAR Cöfkeyle Rdevam etmekte Template pattern (CRTP), C++’da yaygın olarak kullanılan bir deyimdir. Son makalem olan “C++23: Bunun Çıkarımını Yapmak Açık İşaretçiler Oluşturur” başlıklı makalemde tanıttığım klasik Viistor tasarım modelini anlamak kadar zor. Deduction This sayesinde kısaltmadan C ve R’yi çıkarabiliriz.

Duyuru








Rainer Grimm, uzun yıllardır yazılım mimarı, ekip lideri ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanır, aynı zamanda sık sık uzmanlık konferanslarında konuşmaktan da keyif alır. Modernes C++ blogunda yoğun bir şekilde C++ tutkusundan bahsediyor.













CRT uzantısı


CRTP kısaltması, C++ deyiminin kısaltmasıdır Cöfkeyle Rdevam etmekte Tmodeli Pdesen ve C++’da bir sınıfın bulunduğu bir tekniği belirtir ve Derived bir sınıf şablonundan Base türetilmiştir. kilit nokta şu ki Base Derived bir model argümanı olarak.


template <typename T>
class Base{
...
};

class Derived : public Base<Derived>{
...
};


CRTP tipik olarak statik polimorfizmi uygulamak için kullanılır. Dinamik polimorfizmden farklı olarak, bu derleme zamanında gerçekleşir ve hiçbirini gerektirmez. pahalı dolaylı işaretçi.

C++98

Duyuru

Aşağıdaki program crtp.cpp C++98 tabanlı CRTP’nin deyimsel bir uygulamasını sunar.


// crtp.cpp

#include <iostream>

template <typename Derived>
struct Base{
void interface(){ // (2)
static_cast<Derived*>(this)->implementation();
}
void implementation(){ // (3)
std::cout << "Implementation Base" << 'n';
}
};

struct Derived1: Base<Derived1>{
void implementation(){
std::cout << "Implementation Derived1" << 'n';
}
};

struct Derived2: Base<Derived2>{
void implementation(){
std::cout << "Implementation Derived2" << 'n';
}
};

struct Derived3: Base<Derived3>{}; // (4)

template <typename T> // (1)
void execute(T& base){
base.interface();
}

int main(){

std::cout << 'n';

Derived1 d1;
execute(d1);

Derived2 d2;
execute(d2);

Derived3 d3;
execute(d3);

std::cout << 'n';

}


fonksiyon modeli execute (1) statik polimorfizm kullanın. üye işlevi Base::interface (2) CRTP deyiminin anahtarıdır. Üye işlevi, çağrıyı türetilmiş sınıf uygulamasına iletir: static_cast<Derived*>(this)->implementation. Bu mümkündür, çünkü işlev çağrılana kadar başlatılmaz. Bu noktada, türetilmiş sınıflar Derived1, Derived2 VE Derived3 tamamen tanımlanmış. bu nedenle fonksiyon Base::interface türetilmiş sınıfların uygulamasını kullanın. Üye işlevi ilginç Base::implementation (3). Sınıf statik polimorfizmi için varsayılan bir uygulamanın rolünü oynar. Derived3 (4). Aşağıdaki görüntü, eylem halindeki statik polimorfizmi göstermektedir.








C++23

Açık konu parametresi sayesinde, CRTP kısaltmasından C ve R kaldırılabilir.








program deducingThisCRTP.cpp C++23 tabanlı uygulama sağlar CRÖnce TP.


// deducingThisCRTP.cpp

#include <iostream>

struct Base{ // (1)
template <typename Self>
void interface(this Self&& self){
self.implementation();
}
void implementation(){
std::cout << "Implementation Base" << 'n';
}
};

struct Derived1: Base{
void implementation(){
std::cout << "Implementation Derived1" << 'n';
}
};

struct Derived2: Base{
void implementation(){
std::cout << "Implementation Derived2" << 'n';
}
};

struct Derived3: Base{};

template <typename T>
void execute(T& base){
base.interface(); // (2)
}

int main(){

std::cout << 'n';

Derived1 d1; // (3)
execute(d1);

Derived2 d2; // (4)
execute(d2);

Derived3 d3; // (5)
execute(d3);

std::cout << 'n';

}


Açık nesne parametreleri, türetilmiş türü anlamanıza ve onu mükemmel bir şekilde iletmenize olanak tanır (1). (2)’deki beton tipi için Derived1 (3), Derived2 (4) ve Derived3 (5) kullanmak. Sonuç olarak, muhabir sanal işlev implementation isminde: std::forward<Self>(self).implementation(). Program, mevcut Microsoft derleyicisi ile zaten çalıştırılabilir.








özyinelemeli lambdalar


Son Almanca gönderimde, Bunu Çıkarmak: Özyinelemeli Lambdalar’ın daha açıklayıcı kullanımlarını unuttuğuma dair bir yorum aldım. Dürüst olmak gerekirse, Bunun Çıkarımını yapmanın en iyi kullanımının bu olduğundan pek emin değilim çünkü çoğu programcı özyineleme ile mücadele ediyor. İkincisi, karmaşık lambdaların hayranı değilim. Lambdalar özlü ve kendini belgeleyen olmalıdır.

Şimdi yinelemeli olarak tanımlanmış bir faktöriyel fonksiyonun çeşitli uygulamalarını sunuyorum. Bundan sonra, herkes hangi versiyonun okunması en kolay olduğuna kendisi karar verebilir.

Her fonksiyon 10 faktöriyelini hesaplar: 3628800.

C++98

C++98’de iki seçeneğiniz vardır: özyinelemeli örnekleme ile şablon meta programlamayı kullanırsınız veya bir işlev çağrısı kullanırsınız. Şablon metaprogramı derleme zamanında çalışır.


// factorial_cpp98.cpp

#include <iostream>

template <unsigned int N>
struct Factorial{
static int const value = N * Factorial<N-1>::value;
};



template <>
struct Factorial<0>{
static int const value = 1;
};

int factorial(unsigned int n){
return n > 0 ? n * factorial(n - 1): 1;
}

int main(){

std::cout << 'n';

std::cout << "Factorial<10>::value: "
<< Factorial<10>::value << 'n';
std::cout << "factorial(10) "
<< factorial(10) << 'n';

std::cout << 'n';

}


Şablon metaprogramı durumunda, 2 ila 10 arasındaki değerler için eksiksiz bir şablon uzmanlığı oluşturulur: https://cppinsights.io/s/b7a2cbd6.

C++11

C++11’de faktöriyel işlevi constexpr ve derleme zamanında çalışma potansiyeline sahiptir.


// factorial_cpp11.cpp

#include <iostream>

constexpr int factorial(unsigned int n){
return n > 0 ? n * factorial(n - 1): 1;
}

int main(){

std::cout << 'n';

std::cout << "factorial(10) " << factorial(10) << 'n';

std::cout << 'n';

}


C++17

Sayesinde constexp if bağlı olarak farklı kod üretilecektir. N > 0 öyle ya da değil. C++11’deki şablon metaprogramında olduğu gibi, derleyici 2 ila 10 arasındaki değerler için tamamen özel şablonlar oluşturur: https://cppinsights.io/s/650b62f0.


// factorial_cpp17.cpp

#include <iostream>

template <unsigned int N>
constexpr int factorial() {
if constexpr (N > 0)
return N * factorial<N - 1>();
else
return 1;
}

int main(){

std::cout << 'n';

std::cout << "factorial<10>() " << factorial<10>() << 'n';

std::cout << 'n';

}


Bir constexprişlevi (C++11) derleme zamanı potansiyeline sahiptir, ancak bir consteval(C++20) işlevi derleme zamanında yürütülmelidir.

C++20


// factorial_cpp20.cpp

#include <iostream>

consteval int factorial(unsigned int n){
return n > 0 ? n * factorial(n - 1): 1;
}

int main(){

std::cout << 'n';

std::cout << "factorial(10) " << factorial(10) << 'n';

std::cout << 'n';

}


Sonunda C++ 23’te bitirdim. C++23’te bir lambda kendisine atıfta bulunabilir. Bu, özyinelemeli bir lambda uygulamamı sağlıyor.

C++23


// factorial_cpp23.cpp

#include <iostream>

auto factorial = [](this auto&& self, unsigned int n) -> int {
return n > 0 ? n * self(n - 1): 1;
};

int main(){

std::cout << 'n';

std::cout << "factorial(10) " << factorial(10) << 'n';

std::cout << 'n';

}


MSVC derleyicisi bu çıkarımı henüz tam olarak desteklemiyor. Bu yüzden dönüş tipini kullanmak zorundayım (-> int) özyinelemeli lambdanın. P0847R7 önerisine göre bu gerekli değildir.


auto factorial = [](this auto&& self, unsigned int n) {
return n > 0 ? n * self(n - 1): 1;
};


İşte programın çıktısı:








Herkes faktöriyel fonksiyonun farklı bir varyantını tercih edebilir. Benim favorim aktif C++20 versiyonu consteval temelli.

Sıradaki ne?


Çekirdek C++23 dili, Bunu Çıkartmaktan daha fazla işlevsellik sunar. Bu özellikler bir sonraki yazımın konusu olacak.


(rm)



Haberin Sonu
 
Üst