C ++ 23: Açık bölümlerin bu oluşturulmasını çıkarın
Önemli bir C ++ standardının sadece bir küçük olanı takip ettiğini düşünen herkes yanlıştır. C ++ 23, C ++ 20'ye güçlü uzantılar sunar. Bu uzantılar ana dili ve her şeyden önce standart kütüphaneyi içerir. Bugün ana dilin küçük ama çok etkili bir özelliğini sunuyorum: bunu çıkarmak için.
Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.
Bunu, bazen açık nesnelerin parametreleri olarak adlandırılan, örtük olanı etkinleştirin this-Üye işlevinin açık işaretçisi. Python'a benzer şekilde, açık nesnenin parametresi ilk işlevsel parametre olmalı ve anlaşma ile yapılacaktır. Self VE self isminde. Şu anda, yalnızca bunu destekleyen bir mevcut Windows derleyicisi.
struct Test {
void implicitParameter(); // implicit this pointer
void explictParameter(this Self& self); // explicit this pointer
};
C ++ 23'teki bu yeni programlama teknolojisi, nesnenin lValue/rValue değerleri ve sabiti kategorisine göre fonksiyonel aşırı yükleme sağlar. Bunun kesintisi sayesinde, bir lambda referans verilebilir veya CTP (merakla tekrarlayan model) çok daha kolay olabilir.
Fonksiyonel aşırı yükün çıkarılması
Bir üye işlevinin lValue/rvalue değeri ve sabit nesne kategorisine göre aşırı yüklenmesi gerektiğini varsayalım. Bu birçok yazı anlamına gelir. Üye işlevi dört kez aşırı yüklenmelidir.
// 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) çizgileri gerekli fonksiyonel aşırı yüklerdir. (1) ve (2) uygun olmayan ve sabit bir lvalue, (3) ve (4) bir uyumsuz ve sabit bir rvalue alır. Basitleştirilmiş, bir lValue, adresin ve geçici bir değerin belirlenebileceği bir değerdir. (5) A (8) karşılık gelen nesnelerdir. this (9) dört aşırı yükün bir üye işlevinde çıkarmasına izin verir self (10) Mükemmel yönlendirilmiş (mükemmel yönlendirme) e implicitCall çağrılar. Bu makale mükemmel yönlendirme incelikleri: mükemmel yönlendirme ile ilgilidir. Aşağıdaki ekran görüntüsü, ana işlevdeki dört fonksiyonel çağrının, işlevin dört farklı aşırı yükü olduğunu göstermektedir. implicitCall kullanmak.
Tabii ki, sunulan örnek çok akademikti. Ama bu şimdi değişiyor.
Bir lambda işlevine referans
Ziyaretçi modelinin belirleyici fikri, bir nesne hiyerarşisinde operasyonlar gerçekleştirmektir. Nesnelerin hiyerarşisi bu klasik modelde sabittir, ancak operasyonlar genellikle değişebilir.
Ziyaretçi Modeli
Aşağıdaki program visitor.cpp Ziyaretçilerin modelini harekete geçirir.
// 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
{ }
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şında main-Aracın tüm bileşenlerine ulaşmak oluşturulur. Öyleyse al engine Ve bu car . carElementPrintVisitor Su (1) ve (2). (3) ve (4) içinde her iki nesne carElementDoVisitor varsayıldı. CarElement (5) e CarElementVisitor (6) Nesnelerin hiyerarşisinin ve cerrahi hiyerarşinin soyut temel sınıflarıdır. Araba en ilginç bileşendir çünkü bileşenlerini bir std::vector<Element*> (7). Ziyaretçi modelinin belirleyici gözlemi, işlemin gerçekleştirildiği iki nesneye bağlı olmasıdır: ziyaretçi ve ziyaret edilen nesne.
Aşağıdaki ekran görüntüsü programın baskısını göstermektedir:
Yazılımın geliştirilmesindeki makale modelleri bu model hakkında daha fazla bilgi sunmaktadır: ziyaretçilerin modeli. Tabii ki, ziyaretçi modeli çok dirençli. Bu, aşırı yük modeli sayesinde C ++ 23 ile değişir.
Aşırı yük deseni
Aşırı yük modeli, ziyaretçi modelinin modern C ++ versiyonudur. Çeşitli modelleri birleştirin std:variant ve işlevi std::visit. Bunu C ++ 23'te çıkardığım için, 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:
perator()...;
};
class Wheel {
public:
Wheel(const std::string& n): name
{ }
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) Nesnelerin hiyerarşisi ve iki işlem anlamına gelir carElementPrintVisitor (2) e carElementDoVistor (3) Ziyaretçiler için. (4) ve (5) 'de lambda ifadeleri, Car Lambda Empicity nesnesini ziyaret etmek, arabanın belirli bileşenlerini yönlendirebilir ve bu nedenle ziyaret edebilir: car.visitCarElement(self).
Masrafları visitor.cpp VE visitOverload.cpp aynıdır:
Sırada ne var?
Meraklı tekrarlayan model modeli (CRTP) C ++ 'da kullanılan bir dildir. Tasarım modelinin klasik ziyaretçisinin nasıl olduğunu anlamak zor. Bundan düştüğü için C ve R'yi C ++ 23'teki kısaltmadan kaldırabiliriz.
(RME)
C ++ 23: Açık bölümlerin bu oluşturulmasını çıkarın
Önemli bir C ++ standardının sadece bir küçük olanı takip ettiğini düşünen herkes yanlıştır. C ++ 23, C ++ 20'ye güçlü uzantılar sunar. Bu uzantılar ana dili ve her şeyden önce standart kütüphaneyi içerir. Bugün ana dilin küçük ama çok etkili bir özelliğini sunuyorum: bunu çıkarmak için.

Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.

Bunu, bazen açık nesnelerin parametreleri olarak adlandırılan, örtük olanı etkinleştirin this-Üye işlevinin açık işaretçisi. Python'a benzer şekilde, açık nesnenin parametresi ilk işlevsel parametre olmalı ve anlaşma ile yapılacaktır. Self VE self isminde. Şu anda, yalnızca bunu destekleyen bir mevcut Windows derleyicisi.
struct Test {
void implicitParameter(); // implicit this pointer
void explictParameter(this Self& self); // explicit this pointer
};
C ++ 23'teki bu yeni programlama teknolojisi, nesnenin lValue/rValue değerleri ve sabiti kategorisine göre fonksiyonel aşırı yükleme sağlar. Bunun kesintisi sayesinde, bir lambda referans verilebilir veya CTP (merakla tekrarlayan model) çok daha kolay olabilir.
Fonksiyonel aşırı yükün çıkarılması
Bir üye işlevinin lValue/rvalue değeri ve sabit nesne kategorisine göre aşırı yüklenmesi gerektiğini varsayalım. Bu birçok yazı anlamına gelir. Üye işlevi dört kez aşırı yüklenmelidir.
// 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) çizgileri gerekli fonksiyonel aşırı yüklerdir. (1) ve (2) uygun olmayan ve sabit bir lvalue, (3) ve (4) bir uyumsuz ve sabit bir rvalue alır. Basitleştirilmiş, bir lValue, adresin ve geçici bir değerin belirlenebileceği bir değerdir. (5) A (8) karşılık gelen nesnelerdir. this (9) dört aşırı yükün bir üye işlevinde çıkarmasına izin verir self (10) Mükemmel yönlendirilmiş (mükemmel yönlendirme) e implicitCall çağrılar. Bu makale mükemmel yönlendirme incelikleri: mükemmel yönlendirme ile ilgilidir. Aşağıdaki ekran görüntüsü, ana işlevdeki dört fonksiyonel çağrının, işlevin dört farklı aşırı yükü olduğunu göstermektedir. implicitCall kullanmak.

Tabii ki, sunulan örnek çok akademikti. Ama bu şimdi değişiyor.
Bir lambda işlevine referans
Ziyaretçi modelinin belirleyici fikri, bir nesne hiyerarşisinde operasyonlar gerçekleştirmektir. Nesnelerin hiyerarşisi bu klasik modelde sabittir, ancak operasyonlar genellikle değişebilir.
Ziyaretçi Modeli
Aşağıdaki program visitor.cpp Ziyaretçilerin modelini harekete geçirir.
// 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
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şında main-Aracın tüm bileşenlerine ulaşmak oluşturulur. Öyleyse al engine Ve bu car . carElementPrintVisitor Su (1) ve (2). (3) ve (4) içinde her iki nesne carElementDoVisitor varsayıldı. CarElement (5) e CarElementVisitor (6) Nesnelerin hiyerarşisinin ve cerrahi hiyerarşinin soyut temel sınıflarıdır. Araba en ilginç bileşendir çünkü bileşenlerini bir std::vector<Element*> (7). Ziyaretçi modelinin belirleyici gözlemi, işlemin gerçekleştirildiği iki nesneye bağlı olmasıdır: ziyaretçi ve ziyaret edilen nesne.
Aşağıdaki ekran görüntüsü programın baskısını göstermektedir:

Yazılımın geliştirilmesindeki makale modelleri bu model hakkında daha fazla bilgi sunmaktadır: ziyaretçilerin modeli. Tabii ki, ziyaretçi modeli çok dirençli. Bu, aşırı yük modeli sayesinde C ++ 23 ile değişir.
Aşırı yük deseni
Aşırı yük modeli, ziyaretçi modelinin modern C ++ versiyonudur. Çeşitli modelleri birleştirin std:variant ve işlevi std::visit. Bunu C ++ 23'te çıkardığım için, 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:
};
class Wheel {
public:
Wheel(const std::string& n): name
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) Nesnelerin hiyerarşisi ve iki işlem anlamına gelir carElementPrintVisitor (2) e carElementDoVistor (3) Ziyaretçiler için. (4) ve (5) 'de lambda ifadeleri, Car Lambda Empicity nesnesini ziyaret etmek, arabanın belirli bileşenlerini yönlendirebilir ve bu nedenle ziyaret edebilir: car.visitCarElement(self).
Masrafları visitor.cpp VE visitOverload.cpp aynıdır:

Sırada ne var?
Meraklı tekrarlayan model modeli (CRTP) C ++ 'da kullanılan bir dildir. Tasarım modelinin klasik ziyaretçisinin nasıl olduğunu anlamak zor. Bundan düştüğü için C ve R'yi C ++ 23'teki kısaltmadan kaldırabiliriz.
(RME)