Ключевое слово static в php
Содержание:
- Введение
- Переменные
- Преобразование типов данных в C#
- Статический класс
- Подготовка к подключению
- 1 Статические методы
- Когда использовать каждый из методов?
- Наследование статических методов
- Методы Java — как вызывать и использовать
- Свойства
- Ключевое слово static
- Инициализация статических переменных-членов внутри тела класса
- Готовимся к работам
- Полиморфизм
- Статические методы не имеют указателя *this
- Использование статических переменных-членов класса
- Инициализация статических полей
- Классы памяти переменных
- Итоговый наследник message_holder_t
- А что было бы, если бы…
- Заключение
Введение
В Java статическое ключевое слово в основном используется для управления памятью. Может использоваться с переменными, методами, блоками и вложенными классами. Это ключевое слово, которое используется для совместного использования одной и той же переменной или метода данного класса. В основном, static используется для постоянной переменной или метода, который одинаков для каждого экземпляра класса. Основной метод класса обычно помечается как статический.
Чтобы создать статический член (блок, переменную, метод, вложенный класс), вам необходимо предшествовать его объявлению с ключевым словом static. Когда член класса объявлен как статический, к нему можно получить доступ до того, как будут созданы объекты его класса, и без какой-либо ссылки на объект.
В языке программирования Java ключевое слово static является модификатором отсутствия доступа и может использоваться для следующих целей:
- Статический блок.
- переменная.
- метод.
- классы.
Переменные
В контексте статической функции переменные сохраняют свое значение даже после выхода программы из области видимости содержащей их функции. В контексте метода класса статические переменные обладают дополнительным поведением, подобным статическим свойствам: изменения их значений отражаются во всех экземплярах класса. Несмотря на схожесть со статическими свойствами, статические переменные доступны только в теле функции или метода.
Статические переменные часто используются в технике оптимизации под названием мемоизация. Ее целью является ускорение дорогостоящей операции за счет кэширования результатов и сохранения их для последующего вызова с теми же параметрами.
В данном примере мы создаем уникальный для предоставленных параметров хеш и используем его для уникальной идентификации вызова в качестве ключа. Если значение в качестве индекса для не найдено, то выполняем и сохраняем его вывод в индексе , принадлежащему . Каждый последующий вызов для с теми же параметрами будет обходить вызов и возвращать значение из предыдущего вызова:
Преобразование типов данных в C#
Преобразование типов данных, это приведение одного типа к другому. Например, приведение целочисленной переменной к числу с плавающей точной, или преобразование числа в строку. В C# выделяют два варианта преобразования типов:
- Неявное преобразование типов. Это, так называемое безопасное преобразование типов в C#. Например, преобразование из типа float (более «маленький» тип) в тип double (более «большой» тип). При таком преобразовании никакая информация не «потеряется», что при обратном преобразовании вполне возможно.
- Явное преобразование типов. Такое преобразование выполняется программистом с прямым указанием типа, к которому нужно привести переменную. Для такого преобразования требуется наличие оператора преобразования.
А теперь, я покажу как можно использовать явное преобразование типов в C#:
using System; namespace TypesConvertion { class Program { static void Main(string[] args) { //Число с плавающей точкой double doubleData = 245.45; //Вывод этого числа Console.WriteLine(doubleData); //А теперь, мы сделаем на основе этого числа, цело число int intData = (int)doubleData; //Вывод целого числа Console.WriteLine(intData); //Чтобы окно не закрылось раньше времени Console.ReadKey(); } } }
Преобразование типа double в тип int выполнялось в выделенной строке (строке с номером 16). Если вы соберете данный пример и запустите его, то увидите, что преобразование типов прошло, т.е. из числа с плавающей точкой мы получили целое число.
Так же, для преобразования данных из одного типа в другой, есть целый класс с именем Convert, у которого имеются специальные методы. Ниже, в таблице, представлены основные методы этого класса.
Название метода | Целевой тип |
---|---|
ToBoolean | bool |
ToByte | byte |
ToChar | char |
ToDouble | double |
ToSingle | float |
ToInt32 | int |
ToInt64 | long |
ToString | string |
А теперь, пример использования класса Convert и его методов:
using System; namespace TypesConvertion { class Program { static void Main(string[] args) { //Число с плавающей точкой double doubleData = 245.45; //Вывод этого числа Console.WriteLine(doubleData); //А теперь, мы сделаем на основе этого числа, цело число int intData = Convert.ToInt32(doubleData); //Вывод целого числа Console.WriteLine(intData); //Чтобы окно не закрылось раньше времени Console.ReadKey(); } } }
Как видите, это практически тот же код, что был в первом примере, но с использованием другого подхода в преобразовании типов.
Пользоваться возможность преобразования типов вам придется довольно часто!
Статический класс
Класс можно сделать статическим, только если он является вложенным классом. Вложенный статический класс не нуждается в ссылке на Outer. В этом случае статический класс не может получить доступ к нестатическим членам класса Outer. Давайте рассмотрим пример, чтобы понять, как это работает
public class NestedExample{ private static String str= "Edureka" //Static class static class MyNestedClass{ //non-static method public void disp(){ System.out.println(str); } } public static void main(String args[]){ NestedExample.MyNestedClass obj = new NestedExample.MyNestedClass(); obj.disp(); }
Когда вы выполняете приведенный выше код, ваш вывод выглядит так:
Подготовка к подключению
Прежде чем приступить к подключению, следует проверить комплектацию варочной панели.
В некоторых случаях в комплекте уже находиться кабель для подключения, но без вилки
Если кабель идет в комплекте, то обязательно обратите внимание на сечение кабеля, вероятно, оно будет не более 2,5мм2. Такое сечение предназначено для трехфазной сети, и для сети 220 не подойдет
Если кабеля в комплекте нет, то его следует приобрести самостоятельно.
Обычно при подключении используется провод ПВС (рекомендую «нашенских» производителей — «Кобринагромаш», «Автопровод»):
- При подключении к сети 220-240 Вольт (одна фаза + ноль) – ПВС 3×4 (3 жилы с сечением 4мм2)
- При подключении к сети 380-400 Вольт (две фазы + ноль) – ПВС 4×2,5 (4 жилы с сечением 2,5мм2)
- При подключении к сети 380-400 Вольт (три фазы + ноль) – ПВС 5×2,5 (4 жилы с сечением 2,5мм2)
В нашем случае, для квартиры используем ПВС 3×4мм2
Длина соединительного кабеля, должна быть выбрана так, чтобы его было достаточно для свободного подключения (без какой-либо натяжки) от клемм варочной панели до силовой розетки. В большинстве случаев достаточно 1,5 — 2 метра провода.
Для подключения к силовому разъему и клеммам панели, ПВС следует разделать с одной и другой стороны примерно на 12-15 см. Жилы проводов зачищаются на 10 мм.
Так как ПВС представляет собой многожильные провода, то для лучшего контакта зачищенные провода следует обжимать гильзовыми наконечниками (например НШВИ 4,0-10). Для обжима следует применять специальный инструмент — обжимные клещи. Обжимать с помощью плоскогубцев — НЕ ДОПУСКАЕТСЯ (будет плохой обжим).
Проверить насколько качественно вы обжали провод, довольно легко. Просто попробуйте снять гильзу. При хорошем обжиме, у вас вряд ли это получиться.
Для подключения «варочной» к однофазной сети (220-240 Вольт) используем трехконтактную розетку внутреннего исполнения (Bylectrica, арт.РС32-006) и вилку на 32 А (Bylectrica, арт. В32-001).
Осторожно разделываем кабель, чтобы не повредить жилы. Внутри разъема оставляем длину проводов примерно столько же, сколько показано на фото
Провода зачищаем и обжимаем гильзовыми наконечниками.
Перед подключением вилки следует узнать, на каком контакте розетки находиться фаза (слева или справа). Это необходимо, для соблюдения правил цветовой маркировки. В моем случае фазный контакт находиться справа, значит, и правый контакт вилки будет фазным. К нему подключаем провод коричневого цвета или с коричневой полоской. Верхний контакт предназначен для заземления (в старых домах — зануление), к нему подключается желто-зеленый провод. Оставшийся провод голубого цвета или с голубой полоской, подключается к левому контакту – этот ноль.
Для лучшего контакта конец жилы подсоединяется с левой стороны винтового зажима, это препятствует выдавливанию жилы из-под крепления.
Собираем вилку:
- Зажимаем винты (будьте осторожны, не применяйте чрезмерное усилие, в таких розетках легко сорвать резьбу).
- Подтягиваем кабель до упора, чтобы внешняя оболочка кабеля оказалось под зажимным механизмом.
- Надежно фиксируем кабель.
1 Статические методы
Кроме статических переменных, в классах могут быть и статические методы.
Обычные методы привязаны к объектам (экземплярам) класса и могут обращаться к обычным-переменным класса (а также к статическим переменным и методам). Статические же методы привязаны к статическому объекту класса и могут обращаться только к статическим переменным и/или другим статическим методам класса.
Чтобы вызвать обычный метод у класса, сначала нужно создать объект этого класса, а только потом вызвать метод у объекта. Вызвать обычный метод не у объекта, а у класса нельзя.
Пример:
Вызвать нестатический метод у класса нельзя! |
---|
А чтобы вызвать статический метод, достаточно чтобы просто существовал статический объект класса (который всегда существует после загрузки класса в память). Именно поэтому метод main() — статический. Он привязан к статистическому объекту класса, для его вызова не нужно создавать никакие объекты.
Чтобы объявить метод статическим, нужно перед заголовком метода написать ключевое слово static. Общий вид этой конструкции такой:
Примеры:
Код | Примечание |
---|---|
Метод вызывается Java-машиной командой вида: ; Статический метод вызывается в статическом методе . |
Чтобы вызвать статический метод из другого класса, нужно указать имя класса перед именем статического метода. Общий вид этой конструкции такой:
Примеры:
Код | Статический метод |
---|---|
Когда использовать каждый из методов?
Выбор того, какой из методов использовать, может показаться достаточно сложным. Тем не менее с опытом этот выбор делать гораздо проще.
Чаще всего метод класса используется тогда, когда нужен генерирующий метод, возвращающий объект класса. Как видим, метод класса используется для создания объекта класса по году рождения, а не возрасту.
Статические методы в основном используются как вспомогательные функции и работают с данными, которые им передаются.
Запомнить
- Методы экземпляра класса получают доступ к объекту класса через параметр и к классу через .
- Методы класса не могут получить доступ к определённому объекту класса, но имеют доступ к самому классу через .
- Статические методы работают как обычные функции, но принадлежат области имён класса. Они не имеют доступа ни к самому классу, ни к его экземплярам.
- Даже если вы программируете только ради интереса, изучение ООП в Python поможет писать код так, чтобы в дальнейшем было легче искать ошибки и использовать его повторно.
- Давайте синхронизировать потоки в Python
- Овладей Python, создавая реальные приложения. Часть 1
- Полезные хитрости на Python от А до Я
- Изучение Python: от нуля до мастера
Перевод статьи Rachit TayalPython’s Instance, Class and Static Methods
Наследование статических методов
Но как будут
вести себя статические методы при наследовании классов? Предположим, что мы
хотим добавить еще одного специализированного пользователя Admin:
class Admin extends Users { constructor(name, old, login, psw) { super(name, old); this.login = login; this.psw = psw; } }
Мы расширяем
базовый класс Users и добавляем еще
два свойства: login и psw. Будет ли
статический метод compareOld доступен в дочернем классе Admin? Да, будет и,
далее, мы можем создать такого пользователя:
let u2 = new Admin("Федор", 19, "aaa", "0123");
и сравнить их,
вызывая статический метод через класс Admin:
console.log( Admin.compareOld(u1, u2) );
То есть, метод compareOld можно вызывать
и через класс Users и через класс Admin. Разницы
никакой не будет. Это происходит по той причине, что свойство __proto__ класса Admin ссылается на
класс Users:
Если же добавить
еще один статический метод, но уже в класс Admin:
static createAdmin(name, old) { return new this(name, old, "admin", "root"); }
то картина будет
такой:
В методе createAdmin мы создаем
нового пользователя Admin с использованием только двух
параметров: name, old. Остальные два
задаются по умолчанию как: «admin», «root». Причем,
ключевое слово this здесь будет ссылаться на класс, указанный перед
точкой, при вызове данного метода. Например:
let u3 = Admin.createAdmin("Сергей", 33);
Здесь this ссылается на Admin, поэтому будет
создан новый объект класса Admin. А вот если мы в методе compareOld
добавим вывод:
console.log(this == Admin);
и вызовем его
двумя способами:
Users.compareOld(u1, u2); // false Admin.compareOld(u1, u2); // true
то в первом
случае this будет ссылаться
на Users, а во втором –
на Admin.
Методы Java — как вызывать и использовать
}
Другой класс — static, делает функцию видимой только внутри своего модуля.
Рассмотрим пример – у нас будет, как обычно 2 файла, File1.h и File1.c. В первом определим две функции, одну extern, а вторую static
#ifndef _FILE1_H_ #define _FILE1_H_ #include <stdio.h> void visible(); static void hidden(); #endif #include «File1.h» void visible() { printf(«Everyone can use me!\n»); hidden(); } void hidden() { printf(«No one can use me, except my friends from File1.c\n»); }
Заметьте: мы не сможем вызвать функцию hidden вне файла File1.c, но внутри файла эта функция доступна.
#include <conio.h> #include <stdio.h> #include «File1.h» void main() { visible(); //hidden(); её теперь не вызвать getch(); }
Для функций также, как и для переменных, различают объявление и определение. Объявление обычно прячут в заголовочный файл, также как и объявление переменных. Определения находятся в си файле.
ru-Cyrl18-tutorialSypachev S.S.1989-04-14sypachev_s_s@mail.ruStepanSypachevstudents
Свойства
В отличие от обычных свойств, изменение значения статического свойства во время выполнения программы повлияет на все экземпляры содержащего свойство класса. Даже на те, для которых еще не созданы экземпляры. Таким образом, статические свойства можно рассматривать в качестве «констант изменяемого класса». На статические свойства можно ссылаться только с помощью оператора разрешения области видимости.
Благодаря природе статических свойств их можно использовать для реализации шаблона одиночка (Singleton). Одиночка содержит один и тот же экземпляр класса на протяжении всего выполнения программы.
В данном примере первый вызов создает и назначает экземпляр для и возвращает его. Каждый последующий вызов будет возвращать один и тот же экземпляр , ранее присвоенный :
Ключевое слово static
Ключевое слово static может использоваться в различных контекстах, однако сегодня речь пойдет о его использовании именно в классах C#. В русской литературе по программированию на языке C# можно встретить такие определения, как «статичный», «статический» и так далее. В любом случае, это означает, что какой-либо член класса (свойство, метод) определен с атрибутом .
В документации Microsoft по языку C# дословно сказано следующее:
Модификатор static используется для объявления статического члена, принадлежащего собственно типу, а не конкретному объекту.Microsoft
Посмотрим, что это означает на практике.
Инициализация статических переменных-членов внутри тела класса
Есть несколько обходных путей определения статических членов внутри тела класса. Во-первых, если статический член является константным интегральным типом (к которому относятся и char, и bool) или константным перечислением, то статический член может быть инициализирован внутри тела класса:
class Anything
{
public:
static const int s_value = 5; // статическую константную переменную типа int можно объявить и инициализировать напрямую
};
1 |
classAnything { public staticconstints_value=5;// статическую константную переменную типа int можно объявить и инициализировать напрямую }; |
Поскольку здесь статическая переменная-член является целочисленной константой, то дополнительной строки явного определения вне тела класса уже не требуется.
Во-вторых, начиная с C++11 статические члены constexpr любого типа данных, поддерживающие инициализацию constexpr, могут быть инициализированы внутри тела класса:
#include <array>
class Anything
{
public:
static constexpr double s_value = 3.4; // хорошо
static constexpr std::array<int, 3> s_array = { 3, 4, 5 }; // это работает даже с классами, которые поддерживают инициализацию constexpr
};
1 |
#include <array> classAnything { public staticconstexprdoubles_value=3.4;// хорошо staticconstexprstd::array<int,3>s_array={3,4,5};// это работает даже с классами, которые поддерживают инициализацию constexpr }; |
Готовимся к работам
Работы можно проводить как в гипсокартонных перегородках, которые были предварительно сконструированы, так и в бетонных стенах. Подготовка к работе в любом случае будет идентичной, и включать в себя следующие операции:
- демонтируем старое полотно;
- снимаем дверную коробку;
- демонтируем стойки и наличники;
- очищаем рабочее место от пыли и растворов. Делаем в той стороне, которую необходимо сузить.
Демонтаж
Демонтаж проводится от кирпича или бетонного основания. Только после этого наносим разметку. Разметка наносится следующим образом:
- на пол кладем алюминиевое правило и упираем его в штукатурный слой с обеих сторон;
- маркером проводим две линии, которые будут обозначать продолжение стены после уменьшения;
- далее рисуем на уровне планируемого проема вертикальную линию. Сначала чертим на стене базовую вертикаль с помощью отвесов;
- от линии отступаем нужное расстояние, на которое будем уменьшать.
Аналогичные процедуры проводим и для верхнего откоса.
Полиморфизм
полиморфизмом
В итоге
- выделяйте главные характеристики объекта;
- выделяйте общие свойства и поведение и используйте наследование при создании объектов;
- используйте абстрактные типы для описания объектов;
- старайтесь всегда скрывать методы и поля, относящиеся к внутренней реализации класса.
Статические методы не имеют указателя *this
У статических методов есть две интересные особенности.
Во-первых, поскольку статические методы не привязаны к объекту, то они не имеют скрытого указателя *this! Здесь есть смысл, так как указатель *this всегда указывает на объект, с которым работает метод. Статические методы могут не работать через объект, поэтому и указатель *this не нужен.
Во-вторых, статические методы могут напрямую обращаться к другим статическим членам (переменным или функциям), но не могут напрямую обращаться к нестатическим членам. Это связано с тем, что нестатические члены принадлежат объекту класса, а статические методы — нет!
Использование статических переменных-членов класса
Зачем использовать статические переменные-члены внутри классов? Для присваивания уникального идентификатора каждому объекту класса (как вариант):
#include <iostream>
class Anything
{
private:
static int s_idGenerator;
int m_id;
public:
Anything() { m_id = s_idGenerator++; } // увеличиваем значение идентификатора для следующего объекта
int getID() const { return m_id; }
};
// Мы определяем и инициализируем s_idGenerator несмотря на то, что он объявлен как private.
// Это нормально, поскольку определение не подпадает под действия спецификаторов доступа
int Anything::s_idGenerator = 1; // начинаем наш ID-генератор со значения 1
int main()
{
Anything first;
Anything second;
Anything third;
std::cout << first.getID() << ‘\n’;
std::cout << second.getID() << ‘\n’;
std::cout << third.getID() << ‘\n’;
return 0;
}
1 |
#include <iostream> classAnything { private staticints_idGenerator; intm_id; public Anything(){m_id=s_idGenerator++;}// увеличиваем значение идентификатора для следующего объекта intgetID()const{returnm_id;} };
// Мы определяем и инициализируем s_idGenerator несмотря на то, что он объявлен как private. intAnything::s_idGenerator=1;// начинаем наш ID-генератор со значения 1 intmain() { Anything first; Anything second; Anything third; std::cout<<first.getID()<<‘\n’; std::cout<<second.getID()<<‘\n’; std::cout<<third.getID()<<‘\n’; return; } |
Результат выполнения программы:
Поскольку является общим для всех объектов класса Anything, то при создании нового объекта класса Anything конструктор захватывает текущее значение , а затем увеличивает его для следующего объекта. Это гарантирует уникальность идентификаторов для каждого созданного объекта класса Anything.
Статические переменные-члены также могут быть полезны, когда классу необходимо использовать внутреннюю таблицу поиска (например, массив, используемый для хранения набора предварительно вычисленных значений). Делая таблицу поиска статической, для всех объектов класса создастся только одна копия (нежели отдельная для каждого объекта класса). Это поможет сэкономить значительное количество памяти.
Инициализация статических полей
Чтобы инициализировать статическое поле в C# можно использовать такие варианты:
Например,как мы это сделали в нашем классе:
static int ordNumber = 0;
Использование статического конструктора
Наряду с обычными конструкторами в языке C# могут использоваться статические конструкторы, т.е конструкторы, имеющие модификатор . Такие конструкторы обладают следующими особенностями:
- статический конструктор нельзя перегрузить
- статический конструктор не принимает никаких параметров
- статический конструктор выполняется один раз вне зависимости от того сколько объектов данного класса создано
- исходя из п.3, статический конструктор не должен иметь никаких модификаторов доступа — он вызывается автоматически при создании первого объекта или при первом обращении к статическому члену класса.
Например, в нашем классе можно определить вот такой статический конструктор:
class Building { static int ordNumber; //статический конструктор static Building() { ordNumber = 0; } }
Классы памяти переменных
По умолчанию, локальные переменные имеют класс auto. Такие переменные располагаются на стеке а их область видимости ограничена своим блоком. Запись
#include <conio.h> #include <stdio.h> void main() { int x = 10; { int x = 20; { int x = 30; printf(«%d\n», x); } printf(«%d\n», x); } printf(«%d\n», x); getch(); }
идентична
#include <conio.h> #include <stdio.h> void main() { int auto x = 10; { int auto x = 20; { int auto x = 30; printf(«%d\n», x); } printf(«%d\n», x); } printf(«%d\n», x); getch(); }
Очевидно, что глобальные переменные не могут быть объявлены как auto, потому что располагаются в data-сегменте.
Следующий класс памяти – register. Когда мы определяем регистровую переменную, то мы просим компилятор, чтобы переменная располагалась в регистре, а не в оперативной памяти. Компилятор может сделать переменную регистровой, если позволяют условия (регистры не заняты, и по мнению компилятора это не приведёт к увеличению издержек). Регистровые переменные определяются с помощью служебного слово register перед типом
register int x = 20; register int y = 30;
Так как регистровая переменная не имеет адреса, то к ней не применима операция взятия адреса, это вызовет ошибку во время компиляции. Аргументы функции также могут быть заданы как register. Внутри функции они будут вести себя также, как и регистровые переменные.
Следующий класс памяти – статический. Переменные, объявленные как static, хранятся в data или в bss сегменте. Отличительной чертой является то, что время их жизни совпадает с временем жизни приложения, как и у глобальных переменных. Но в отличие от глобальных переменных, область видимости ограничена только блоком, в котором они определены.
#include <conio.h> #include <stdio.h> unsigned long long factorial(unsigned char n) { static unsigned char prevArg = 0; static long long prevAns = 1; if (n == prevArg) { printf(«return previous answer\n»); return prevAns; } else { unsigned i = 0; printf(«count new answer\n»); prevAns = 1; for (i = 1; i <= n; i++) { prevAns *= i; } prevArg = n; return prevAns; } } void main() { printf(«!%d == %llu\n», 10, factorial(10)); printf(«!%d == %llu\n», 10, factorial(10)); printf(«!%d == %llu\n», 11, factorial(11)); printf(«!%d == %llu\n», 11, factorial(11)); getch(); }
В этом примере переменные prevArg и prevAns инициализируются единожды, и не уничтожаются после выхода из функции. Переменная prevArg используется для хранения предыдущего аргумента функции, а prevAns для хранения предыдущего результата. Если аргумента функции совпадает с предыдущим, то возвращается ранее вычисленное значение, иначе оно вычисляется по-новому.
Другой показательный пример – функция-генератор, которая при каждом вызове возвращает новое значение.
#include <conio.h> #include <stdio.h> int next() { static int counter = 0; counter++; return counter; } void main() { printf(«%d\n», next()); printf(«%d\n», next()); printf(«%d\n», next()); printf(«%d\n», next()); printf(«%d\n», next()); _getch(); }
Если бы служебное слово static отсутствовало, то каждый раз при вызове функции локальная переменная counter снова создавалась, инициализировалась и уничтожалась после выхода из функции.
Статическая переменная может иметь только константную инициализацию. Например, она не может быть инициализирована вызовом функции.
… static double x = foo(3); //Ошибка …
Переменная, объявленная как static, должна иметь только один экземпляр в данной области видимости и вне этой области видимости не видна. Глобальная переменная, объявленная как static, видна только в своём файле.
Напротив, переменная, объявленная как extern может быть использована в других файлах при условии, что она была определена.
Итоговый наследник message_holder_t
Теперь можно посмотреть на то, что же из себя представляет , для реализации которого потребовались все эти базовые классы и метафункции (из реализации удалена часть методов для конструирования экземпляра хранящегося в message_holder-е сообщения):
По сути все то, что мы разбирали выше, потребовалось для того, чтобы записать вот этот «вызов» двух метафункций:
Т.к. это не первый вариант, а результат упрощения и сокращения кода, то могу сказать, что компактные формы метафункций ну очень сильно уменьшают объем кода и увеличивают его понятность (если о понятности здесь вообще уместно говорить).
А что было бы, если бы…
А вот если бы в C++ был настолько же мощен, как в D, то можно было бы написать что-то вроде:
Как по мне, так отличия слишком уж разительны. И они не в пользу текущего C++ 🙁
(разобранный выше C++ный код в виде одной сплошной «портянки» можно увидеть здесь).
Кстати говоря, я не очень сильно слежу за тем, что происходит в области предложений по метапрограммированию и рефлексии для будущих версий С++. Но из того, что помню, складывается ощущение, что предлагавшиеся Саттером метаклассы не очень упростят вот эту конкретную задачу. Как я понимаю, посредством метаклассов можно будет написать генератор классов . Может быть такой генератор получится и несложным в написании, но вряд ли такой подход в данном конкретном случае окажется выразительнее и понятнее, чем в случае действительно продвинутого .
Заключение
Как по мне, так этот пример показывает весь блеск и нищету C++. Да, можно сотворить все что угодно. В смысле, можно сделать шаблонный класс, содержимое которого будет кардинально меняться в зависимости от параметров шаблона.
Но вот чтобы сделать это придется несколько поломать мозги и написать на шаблонах столько вспомогательного кода, что копаться во всем этом не будет желания даже у автора.
Тем не менее, сам факт того, что на С++ можно такое сотворить, меня лично радует. Огорчает количество труда и объем кода, который для этого потребуется. Но, надеюсь, что со временем объем этого кода и его сложность будет только сокращаться. В принципе, это видно уже сейчас. Ибо для C++98/03 я даже не взялся бы такой трюк проделывать, тогда как начиная с C++11 делать подобное становится все проще и проще.