C++23: Bunu Çıkarmak, açık işaretçiler oluşturur

Adanali

Active member
C++23: Bunu Çıkarmak, açık işaretçiler oluşturur


  1. C++23: Bunu Çıkarmak, açık işaretçiler oluşturur

Büyük bir C++ standardının ardından daha küçük bir standardın geldiğini düşünen herkes yanılıyor. C++23, C++20 için güçlü uzantılar sunar. Bu uzantılar, ana dili ve en önemlisi standart kitaplığı içerir. Bugün çekirdek dilin küçük ama çok etkili bir özelliğini tanıtıyorum: Çıkarım Yapmak.

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.













Çıkarım Bu, bazen açık nesne parametresi olarak adlandırılır, örtük this– Bir üye işlevin işaretçilerini açık hale getirin. Python’a benzer şekilde, açık nesne parametresi, işlevin ilk parametresi olmalıdır ve kural gereği, Self VE self isminde. Bu yazı itibariyle, yalnızca yeni bir Windows derleyicisi Deducing This’i desteklemektedir.


struct Test {
void implicitParameter(); // implicit this pointer
void explictParameter(this Self& self); // explicit this pointer
};


C++23’teki bu yeni programlama tekniği, nesnenin değer/değer kategorisine ve sabitliğine dayalı olarak işlev aşırı yüklemesini tekilleştirmenize olanak tanır. Çıkarım Bu, bir lambdaya başvurmayı veya CRTP’yi (Merakla Yinelenen Şablon Kalıbı) uygulamayı çok daha kolaylaştırır.

Tekilleştirme işlevinin aşırı yüklenmesi


Bir üye işlevin, lvalue/rvalue değer kategorisine ve çağıran nesnenin sabitliğine göre aşırı yüklenmesi gerektiğini varsayalım. Bu da bir sürü evrak işi anlamına geliyor. Üye işlevi dört kez aşırı yüklenmelidir.

Duyuru


// deducingThis.cpp

#include <iostream>

struct Test {
template <typename Self>
void explicitCall(this Self&& self,
const std::string& text) { // (9)
std::cout << text << ": ";
std::forward<Self>(self).implicitCall(); // (10)
std::cout << 'n';
}

void implicitCall() & { // (1)
std::cout << "non const lvalue";
}

void implicitCall() const& { // (2)
std::cout << "const lvalue";
}

void implicitCall() && { // (3)
std::cout << "non const rvalue";
}

void implicitCall() const&& { // (4)
std::cout << "const rvalue";
}

};

int main() {

std::cout << 'n';

Test test;
const Test constTest;

test.explicitCall("test"); // (5)
constTest.explicitCall("constTest"); // (6)
std::move(test).explicitCall("std::move(test)"); // (7)
std::move(constTest).explicitCall("std::move(consTest)"); // (8)

std::cout << 'n';

}


(1), (2), (3) ve (4) numaralı satırlar gerekli fonksiyonların aşırı yüklemeleridir. (1) ve (2) sabit olmayan ve sabit bir değer, (3) ve (4) sabit olmayan ve sabit bir değer almaktadır. Basitçe ifade etmek gerekirse, bir değer, adresin belirlenebileceği bir değerdir ve bir değer geçici bir değerdir. (5) ila (8) karşılık gelen nesnelerdir. this (9), bir üye işlevdeki dört aşırı yüklemeyi tekilleştirmenize izin verir. self (10) mükemmel yönlendirme e implicitCall çağrılar. Bu makale, Perfect Forwarding’in daha ince noktalarını ele alıyor: Perfect Forwarding. Aşağıdaki ekran görüntüsü, ana işlevdeki dört işlev çağrısının, işlevin dört farklı aşırı yüklemesi olduğunu iyi bir şekilde göstermektedir. implicitCall kullanım.








Kuşkusuz, sunulan örnek çok akademikti. Ama şimdi bu değişiyor.

Bir Lambda işlevine başvuru


Ziyaretçi modelinin ana fikri, bir nesneler hiyerarşisi üzerinde işlemler gerçekleştirmektir. Bu klasik modelde nesne hiyerarşisi sabittir, ancak işlemler sık sık değişebilir.

ziyaretçi modeli


Aşağıdaki program visitor.cpp ziyaretçi modelini iş başında gösterir.


// visitor.cpp

#include <iostream>
#include <string>
#include <vector>

class CarElementVisitor;

class CarElement { // (5)
public:
virtual void accept(CarElementVisitor& visitor) const = 0;
virtual ~CarElement() = default;
};

class Body;
class Car;
class Engine;
class Wheel;

class CarElementVisitor { // (6)
public:
virtual void visit(Body body) const = 0;
virtual void visit(Car car) const = 0;
virtual void visit(Engine engine) const = 0;
virtual void visit(Wheel wheel) const = 0;
virtual ~CarElementVisitor() = default;
};

class Wheel: public CarElement {
public:
Wheel(const std::string& n): name(n) { }

void accept(CarElementVisitor& visitor) const override {
visitor.visit(*this);
}

std::string getName() const {
return name;
}
private:
std::string name;
};

class Body: public CarElement {
public:
void accept(CarElementVisitor& visitor) const override {
visitor.visit(*this);
}
};

class Engine: public CarElement {
public:
void accept(CarElementVisitor& visitor) const override {
visitor.visit(*this);
}
};

class Car: public CarElement {
public:
Car(std::initializer_list<CarElement*> carElements ):
elements{carElements} {}

void accept(CarElementVisitor& visitor) const override {
for (auto elem : elements) {
elem->accept(visitor);
}
visitor.visit(*this);
}
private:
std::vector<CarElement*> elements; // (7)
};

class CarElementDoVisitor: public CarElementVisitor {

void visit(Body body) const override {
std::cout << "Moving my body" << 'n';
}

void visit(Car car) const override {
std::cout << "Starting my car" << 'n';
}

void visit(Wheel wheel) const override {
std::cout << "Kicking my " << wheel.getName()
<< " wheel" << 'n';
}

void visit(Engine engine) const override {
std::cout << "Starting my engine" << 'n';
}
};

class CarElementPrintVisitor: public CarElementVisitor {

void visit(Body body) const override {
std::cout << "Visiting body" << 'n';
}

void visit(Car car) const override {
std::cout << "Visiting car" << 'n';
}

void visit(Wheel wheel) const override {
std::cout << "Visiting " << wheel.getName()
<< " wheel" << 'n';
}

void visit(Engine engine) const override {
std::cout << "Visiting engine" << 'n';
}
};

int main() {

std::cout << 'n';

Wheel wheelFrontLeft("front left");
Wheel wheelFrontRight("front right");
Wheel wheelBackLeft("back left");
Wheel wheelBackRight("back right");
Body body;
Engine engine;
Car car {&wheelFrontLeft, &wheelFrontRight,
&wheelBackLeft, &wheelBackRight,
&body, &engine};

CarElementPrintVisitor carElementPrintVisitor;

engine.accept(carElementPrintVisitor); // (1)
car.accept(carElementPrintVisitor); // (2)

std::cout << 'n';

CarElementDoVisitor carElementDoVisitor;

engine.accept(carElementDoVisitor); // (3)
car.accept(carElementDoVisitor); // (4)

std::cout << 'n';

}


Başlangıcında main-Fonksiyon, arabanın tüm bileşenlerini oluşturur. ondan sonra alırlar engine ve car ONLAR carElementPrintVisitor (1) ve (2)’de. (3) ve (4)’te her iki nesne de carElementDoVisitor iddia edildi. CarElement (5) ve CarElementVisitor (6), nesne hiyerarşisinin ve işlem hiyerarşisinin soyut temel sınıflarıdır. Araba en ilginç bileşendir çünkü bileşenlerini bir arada tutar. std::vector<Element*> (7). Ziyaretçi modelinin temel gözlemi, gerçekleştirilen işlemin iki nesneye bağlı olmasıdır: ziyaretçi ve ziyaret edilen nesne.

Aşağıdaki ekran görüntüsü programın çıktısını göstermektedir:








Bu kalıp hakkında daha fazla bilgi, Yazılım Geliştirmede Kalıplar: Ziyaretçi Kalıbı makalesinde bulunabilir. Kuşkusuz, ziyaretçi modeli anlaşılmaya çok dirençlidir. Bu, aşırı yükleme modeli sayesinde C++23 ile değişir.

aşırı yük modeli


Aşırı yükleme modeli, ziyaretçi modelinin modern C++ sürümüdür. Variadic şablonlarını şununla birleştirin: std:variant ve işlevi std::visit. C++23’te Bunu Çıkarsama sayesinde, bir lambda ifadesi örtük lambda nesnesini açıkça kullanabilir.


// visitOverload.cpp

#include <iostream>
#include <string>
#include <vector>
#include <variant>

template<class... Ts> struct overloaded : Ts... {
using Ts::eek:perator()...;
};

class Wheel {
public:
Wheel(const std::string& n): name(n) { }
std::string getName() const {
return name;
}
private:
std::string name;
};

class Body {};

class Engine {};

class Car;

using CarElement = std::variant<Wheel, Body, Engine, Car>;

class Car {
public:
Car(std::initializer_list<CarElement*> carElements ):
elements{carElements} {}

template<typename T>
void visitCarElements(T&& visitor) const {
for (auto elem : elements) {
std::visit(visitor, *elem);
}
}
private:
std::vector<CarElement*> elements;
};

overloaded carElementPrintVisitor { // (2)
[](const Body& body)
{ std::cout << "Visiting body" << 'n'; },
[](this auto const& self, const Car& car)
{ car.visitCarElements(self); // (4)
std::cout << "Visiting car" << 'n'; },
[](const Wheel& wheel)
{ std::cout << "Visiting "
<< wheel.getName() << " wheel" << 'n'; },
[](const Engine& engine)
{ std::cout << "Visiting engine" << 'n';}
};

overloaded carElementDoVisitor { // (3)
[](const Body& body)
{ std::cout << "Moving my body" << 'n'; },
[](this auto const& self, const Car& car)
{ car.visitCarElements(self); // (5)
std::cout << "Starting my car" << 'n'; },
[](const Wheel& wheel)
{ std::cout << "Kicking my "
<< wheel.getName() << " wheel" << 'n'; },
[](const Engine& engine)
{ std::cout << "Starting my engine" << 'n';}
};


int main() {

std::cout << 'n';

CarElement wheelFrontLeft = Wheel("front left");
CarElement wheelFrontRight = Wheel("front right");
CarElement wheelBackLeft = Wheel("back left");
CarElement wheelBackRight = Wheel("back right");
CarElement body = Body{};
CarElement engine = Engine{};

CarElement car = Car{&wheelFrontLeft, &wheelFrontRight, // (1)
&wheelBackLeft, &wheelBackRight,
&body, &engine};

std::visit(carElementPrintVisitor, engine);
std::visit(carElementPrintVisitor, car);
std::cout << 'n';

std::visit(carElementDoVisitor, engine);
std::visit(carElementDoVisitor, car);
std::cout << 'n';

}


Car (1) nesne hiyerarşisini ve iki işlemi temsil eder carElementPrintVisitor (2) ve carElementDoVistor (3) ziyaretçiler için. (4) ve (5)’teki lambda ifadeleri Car örtük Lambda nesnesine başvurabilir ve onu arabanın somut bileşenlerini ziyaret etmek için kullanabilir: car.visitCarElement(self).

giderleri visitor.cpp VE visitOverload.cpp Özdeş:









Sıradaki ne?



Curiously Recurring Template Pattern (CRTP), C++’da yaygın olarak kullanılan bir deyimdir. Klasik Ziyaretçi Kalıbını anlamak kadar zor. Deduction This sayesinde C++23 kısaltmasından C ve R harflerini kaldırabiliriz.


(rm)



Haberin Sonu
 
Üst