Урок №56. явное преобразование типов данных

Последовательность шагов и уже готовая шаблонная магия

Итак, нам нужно иметь класс с несколькими наборами методов. Содержимое этих наборов должно откуда-то взяться. Откуда?

В языке D мы могли бы воспользоваться и определить разные части класса в зависимости от разных условий. В каком-нибудь Ruby мы могли бы подмешать методы в свой класс посредством метода include. Но мы в C++, в котором пока наши возможности сильно ограничены: мы можем либо определить метод/атрибут прямо внутри класса, либо можем унаследовать метод/атрибут из какого-то базового класса.

Определить разные методы/атрибуты внутри класса в зависимости от какого-то условия мы не можем, т.к. C++ный — это не D-шный . Следовательно, остается только наследование.

В C++ мы можем определить несколько базовых классов, от которых мы затем отнаследуем . А выбор того или иного базового класса уже будем делать в зависимости от значений параметров шаблона, посредством std::conditional.

Но фокус в том, что нам потребуется не просто набор базовых классов, а небольшая цепочка наследования. В ее начале будет класс, который будет определять общую функциональность, которая потребуется в любом случае. Далее будут базовые классы, которые будут определять логику поведения «умного указателя». А уже затем будет класс, который определит нужные getter-ы. В таком порядке мы и рассмотрим реализованные классы.

Нашу задачу упрощает то, что в SObjectizer-е уже есть готовая шаблонная магия, , а также . Поэтому в реализации мы эту готовую магию будем просто использовать и не станем погружаться в детали ее работы.

Инициализация статических переменных-членов внутри тела класса

Есть несколько обходных путей определения статических членов внутри тела класса. Во-первых, если статический член является константным интегральным типом (к которому относятся и char, и bool) или константным перечислением, то статический член может быть инициализирован внутри тела класса:

class Anything
{
public:
static const int s_value = 5; // статическую константную переменную типа int можно объявить и инициализировать напрямую
};

1
2
3
4
5

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
2
3
4
5
6
7
8

#include <array>

classAnything

{

public

staticconstexprdoubles_value=3.4;// хорошо

staticconstexprstd::array<int,3>s_array={3,4,5};// это работает даже с классами, которые поддерживают инициализацию constexpr

};

Remarks

Static constructors have the following properties:

  • A static constructor does not take access modifiers or have parameters.

  • A class or struct can only have one static constructor.

  • Static constructors cannot be inherited or overloaded.

  • A static constructor cannot be called directly and is only meant to be called by the common language runtime (CLR). It is invoked automatically.

  • The user has no control on when the static constructor is executed in the program.

  • A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced. A static constructor will run before an instance constructor. A type’s static constructor is called when a static method assigned to an event or a delegate is invoked and not when it is assigned. If static field variable initializers are present in the class of the static constructor, they will be executed in the textual order in which they appear in the class declaration immediately prior to the execution of the static constructor.

  • If you don’t provide a static constructor to initialize static fields, all static fields are initialized to their default value as listed in Default values of C# types.

  • If a static constructor throws an exception, the runtime will not invoke it a second time, and the type will remain uninitialized for the lifetime of the application domain in which your program is running. Most commonly, a TypeInitializationException exception is thrown when a static constructor is unable to instantiate a type or for an unhandled exception occurring within a static constructor. For implicit static constructors that are not explicitly defined in source code, troubleshooting may require inspection of the intermediate language (IL) code.

  • The presence of a static constructor prevents the addition of the type attribute. This limits runtime optimization.

  • A field declared as may only be assigned as part of its declaration or in a static constructor. When an explicit static constructor is not required, initialize static fields at declaration, rather than through a static constructor for better runtime optimization.

Note

Though not directly accessible, the presence of an explicit static constructor should be documented to assist with troubleshooting initialization exceptions.

Usage

  • A typical use of static constructors is when the class is using a log file and the constructor is used to write entries to this file.

  • Static constructors are also useful when creating wrapper classes for unmanaged code, when the constructor can call the method.

  • Static constructors are also a convenient place to enforce run-time checks on the type parameter that cannot be checked at compile time via constraints (Type parameter constraints).

3 Удаление и добавление модификатора static

Из статической в обычную

Что будет, если мы возьмем статическую переменную и превратим ее в обычную: удалим у нее модификатор ? Например, у переменной .

Измененный код будет выглядеть так:

А в памяти мы получим такую картину:

Статическая переменная исчезла у статического объекта, а у каждого объекта появилось по своей собственной переменной .

Из обычной в статическую

Можно сделать и наоборот: добавить перед обычными переменными класса модификатор , и тогда они исчезнут у всех объектов и появятся у статического объекта. Допустим, мы решили сделать переменные и статическими. Тогда код будет выглядеть так:

А в памяти мы получим уже такую картину:

ПримерExample

В следующем примере директива используется для того, чтобы доступ к статическим членам классов Console, Math и String можно было получать, не указывая имя типа.The following example uses the directive to make the static members of the Console, Math, and String classes available without having to specify their type name.

В этом примере директива может также применяться к типу Double.In the example, the directive could also have been applied to the Double type. В этом случае вызвать метод , не указав имя типа, было бы возможно.This would have made it possible to call the method without specifying a type name. При этом код становится менее понятным, поскольку появляется необходимость проверять директивы и определять, какой метод числового типа вызывается.However, this creates less readable code, since it becomes necessary to check the directives to determine which numeric type’s method is called.

Еще один пример

Статические методы можно определять вне тела класса. Это работает так же, как и с обычными методами. Например:

#include <iostream>

class IDGenerator
{
private:
static int s_nextID; // объявление статической переменной-члена

public:
static int getNextID(); // объявление статического метода
};

// Определение статической переменной-члена находится вне тела класса

Обратите внимание, мы не используем здесь ключевое слово static.
// Начинаем генерировать ID с 1
int IDGenerator::s_nextID = 1;

// Определение статического метода находится вне тела класса. Обратите внимание, мы не используем здесь ключевое слово static
int IDGenerator::getNextID() { return s_nextID++; }

int main()
{
for (int count=0; count

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

#include
 

classIDGenerator

{

private

staticints_nextID;// объявление статической переменной-члена

public

staticintgetNextID();// объявление статического метода

};

// Определение статической переменной-члена находится вне тела класса

Обратите внимание, мы не используем здесь ключевое слово static.
// Начинаем генерировать ID с 1

intIDGenerator::s_nextID=1;

// Определение статического метода находится вне тела класса. Обратите внимание, мы не используем здесь ключевое слово static

intIDGenerator::getNextID(){returns_nextID++;}

intmain()

{

for(intcount=;count

Результат выполнения программы:

Обратите внимание, поскольку все переменные и функции этого класса являются статическими, то нам не нужно создавать объект этого класса для работы с ним! Статическая переменная-член используется для хранения значения следующего идентификатора, который должен быть ей присвоен, а статический метод — для возврата идентификатора и его увеличения

Статические члены не связаны с объектами класса

Хотя вы можете получить доступ к статическим членам через разные объекты класса (как в примере, приведенном выше), но, оказывается, статические члены существуют, даже если объекты класса не созданы! Подобно глобальным переменным, они создаются при запуске программы и уничтожаются, когда программа завершает свое выполнение.

Следовательно, статические члены принадлежат классу, а не объектам этого класса. Поскольку существует независимо от любых объектов класса, то доступ к нему осуществляется напрямую через имя класса и оператор разрешения области видимости (в данном случае, через ):

#include <iostream>

class Anything
{
public:
static int s_value; // объявляем статическую переменную-член
};

int Anything::s_value = 3; // определяем статическую переменную-член

int main()
{
// Примечание: Мы не создаем здесь никаких объектов класса Anything

Anything::s_value = 4;
std::cout << Anything::s_value << ‘\n’;
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

#include <iostream>
 

classAnything

{

public

staticints_value;// объявляем статическую переменную-член

};

intAnything::s_value=3;// определяем статическую переменную-член

intmain()

{

// Примечание: Мы не создаем здесь никаких объектов класса Anything

Anything::s_value=4;

std::cout<<Anything::s_value<<‘\n’;

return;

}

В вышеприведенном фрагменте, доступ к осуществляется через имя класса, а не через объект этого класса

Обратите внимание, мы даже не создавали объект класса Anything, но мы все равно имеем доступ к и можем использовать эту переменную-член

Что такое статические методы в Java?

Статические методы — это методы в Java, которые можно вызывать без создания объекта класса. Они задокументированы именем {class the category}. Статическое ключевое слово может использоваться с классом, переменной, методом и блоком. Статические члены принадлежат классу, а не конкретному экземпляру, это означает, что если вы сделаете член статическим, вы сможете получить к нему доступ без объекта. Давайте рассмотрим пример, чтобы понять это:

Здесь у нас есть статический метод myMethod(), мы можем вызвать этот метод без какого-либо объекта, потому что когда мы делаем член статическим, он становится уровнем класса. Если мы удалим ключевое слово static и сделаем его нестатичным, нам нужно будет создать объект класса для его вызова.

Статические члены являются общими для всех экземпляров (объектов) класса, но нестатические члены являются отдельными для каждого экземпляра класса.

class SimpleStaticExample
{
    // This is a static method
    static void myMethod()
    {
        System.out.println("myMethod");
    }
 
    public static void main(String[] args)
    {
          /* You can see that we are calling this
           * method without creating any object. 
           */
           myMethod();
    }
}

Синтаксис

public static void geek(String name)
{
// code to be executed....

Он хранится в Permanent Generation, поскольку связывается с {class the category}, где они находятся, а не с объектами этого класса. Тем не менее, их локальные переменные, а также передаваемый им аргумент(ы) находятся в стеке.

Важные моменты:

  • Статический метод(ы), связанный с классом, в котором они находятся, то есть они будут ссылаться на него, даже если он не создает экземпляр класса, т.е. ClassName.methodName (args).
  • Они предназначены для совместного использования всеми объектами, созданными из одного класса.
  • Статические методы не могут быть переопределены.

Пример использования статических методов в Java:

import java.io.*;
class Flair{
   public static String FlairName = "";
   public static void geek(String name)
{
         FlairName = name;
   }
}
class GFG {
   public static void main (String[] args) {
         Flair.flair("vaibhav");
         System.out.println(Flair.flairName);
         Flair obj = new Flair ();
         obj.flair("shadow");
         System.out.println(obj.flairName);
   }
}

Вывод:

Что если статическая переменная ссылается на объект?

В первой строке значение, которое будет храниться в разделе PermGen. Во второй строке ссылка obj будет храниться в секции PermGen, а объект, на который она ссылается, будет храниться в секции heap.

Использование статических переменных-членов класса

Зачем использовать статические переменные-члены внутри классов? Для присваивания уникального идентификатора каждому объекту класса (как вариант):

#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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

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

Статические переменные-члены также могут быть полезны, когда классу необходимо использовать внутреннюю таблицу поиска (например, массив, используемый для хранения набора предварительно вычисленных значений). Делая таблицу поиска статической, для всех объектов класса создастся только одна копия (нежели отдельная для каждого объекта класса). Это поможет сэкономить значительное количество памяти.

Готовимся к работам

Работы можно проводить как в гипсокартонных перегородках, которые были предварительно сконструированы, так и в бетонных стенах. Подготовка к работе в любом случае будет идентичной, и включать в себя следующие операции:

  • демонтируем старое полотно;
  • снимаем дверную коробку;
  • демонтируем стойки и наличники;
  • очищаем рабочее место от пыли и растворов. Делаем в той стороне, которую необходимо сузить.

Демонтаж

Демонтаж проводится от кирпича или бетонного основания. Только после этого наносим разметку. Разметка наносится следующим образом:

  • на пол кладем алюминиевое правило и упираем его в штукатурный слой с обеих сторон;
  • маркером проводим две линии, которые будут обозначать продолжение стены после уменьшения;
  • далее рисуем на уровне планируемого проема вертикальную линию. Сначала чертим на стене базовую вертикаль с помощью отвесов;
  • от линии отступаем нужное расстояние, на которое будем уменьшать.

Аналогичные процедуры проводим и для верхнего откоса.

Статические переменные-члены класса

Из урока №51 мы узнали, что статические переменные сохраняют свои значения и не уничтожаются даже после выхода из блока, в котором они объявлены, например:

#include <iostream>

int generateID()
{
static int s_id = 0;
return ++s_id;
}

int main()
{
std::cout << generateID() << ‘\n’;
std::cout << generateID() << ‘\n’;
std::cout << generateID() << ‘\n’;

return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#include <iostream>

intgenerateID()

{

staticints_id=;

return++s_id;

}

intmain()

{

std::cout<<generateID()<<‘\n’;

std::cout<<generateID()<<‘\n’;

std::cout<<generateID()<<‘\n’;

return;

}

Результат выполнения программы:

Обратите внимание, сохраняет свое значение после каждого вызова функции generateID(). Ключевое слово static имеет другое значение, когда речь идет о глобальных переменных — оно предоставляет им внутреннюю связь (что ограничивает их видимость/использование за пределами файла, в котором они определены)

Поскольку использование глобальных переменных — это зло, то ключевое слово static в этом контексте используется не очень часто

Ключевое слово static имеет другое значение, когда речь идет о глобальных переменных — оно предоставляет им внутреннюю связь (что ограничивает их видимость/использование за пределами файла, в котором они определены). Поскольку использование глобальных переменных — это зло, то ключевое слово static в этом контексте используется не очень часто.

В языке C++ ключевое слово static можно использовать в классах: статические переменные-члены и статические методы. Мы поговорим о статических переменных-членах на этом уроке, а о статических методах на следующем.

Прежде чем мы перейдем к ключевому слову static с переменными-членами класса, давайте сначала рассмотрим следующий класс:

#include <iostream>

class Anything
{
public:
int m_value = 3;
};

int main()
{
Anything first;
Anything second;

first.m_value = 4;

std::cout << first.m_value << ‘\n’;
std::cout << second.m_value << ‘\n’;

return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

#include <iostream>
 

classAnything

{

public

intm_value=3;

};

intmain()

{

Anything first;

Anything second;

first.m_value=4;

std::cout<<first.m_value<<‘\n’;

std::cout<<second.m_value<<‘\n’;

return;

}

При создании объекта класса, каждый объект получает свою собственную копию всех переменных-членов класса. В этом случае, поскольку мы объявили два объекта класса Anything, у нас будет две копии : и . Это разные значения, следовательно, результат выполнения программы:

Переменные-члены класса можно сделать статическими, используя ключевое слово static. В отличие от обычных переменных-членов, статические переменные-члены являются общими для всех объектов класса. Рассмотрим следующую программу:

#include <iostream>

class Anything
{
public:
static int s_value;
};

int Anything::s_value = 3;

int main()
{
Anything first;
Anything second;

first.s_value = 4;

std::cout << first.s_value << ‘\n’;
std::cout << second.s_value << ‘\n’;
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#include <iostream>
 

classAnything

{

public

staticints_value;

};

intAnything::s_value=3;

intmain()

{

Anything first;

Anything second;

first.s_value=4;

std::cout<<first.s_value<<‘\n’;

std::cout<<second.s_value<<‘\n’;

return;

}

Результат выполнения программы:

Поскольку является статической переменной-членом, то она является общей для всех объектов класса Anything. Следовательно, — это та же переменная, что и . Вышеприведенная программа показывает, что к значению, которое мы установили через первый объект, можно получить доступ и через второй объект.

Наследование статических методов

Но как будут
вести себя статические методы при наследовании классов? Предположим, что мы
хотим добавить еще одного специализированного пользователя 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.

Статические методы

Если статические переменные-члены являются открытыми, то мы можем получить к ним доступ напрямую через имя класса и оператор разрешения области видимости. Но что, если статические переменные-члены являются закрытыми? Рассмотрим следующий код:

class Anything
{
private:
static int s_value;

};

int Anything::s_value = 3; // определение статического члена, несмотря на то, что он является private

int main()
{
// Как получить доступ к Anything::s_value здесь, если s_value является private?
}

1
2
3
4
5
6
7
8
9
10
11
12
13

classAnything

{

private

staticints_value;

};

intAnything::s_value=3;// определение статического члена, несмотря на то, что он является private

intmain()

{

// Как получить доступ к Anything::s_value здесь, если s_value является private?

}

В этом случае мы не можем напрямую получить доступ к из функции main(), так как этот член является private. Обычно, доступ к закрытым членам класса осуществляется через public-методы. Хотя мы могли бы создать обычный метод для получения доступа к , но нам тогда пришлось бы создавать объект этого класса для использования метода! Есть вариант получше: мы можем сделать метод статическим.

Подобно статическим переменным-членам, статические методы не привязаны к какому-либо одному объекту класса. Вот вышеприведенный пример, но уже со статическим методом:

class Anything
{
private:
static int s_value;
public:
static int getValue() { return s_value; } // статический метод
};

int Anything::s_value = 3; // определение статической переменной-члена класса

int main()
{
std::cout << Anything::getValue() << ‘\n’;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

classAnything

{

private

staticints_value;

public

staticintgetValue(){returns_value;}// статический метод

};

intAnything::s_value=3;// определение статической переменной-члена класса

intmain()

{

std::cout<<Anything::getValue()<<‘\n’;

}

Поскольку статические методы не привязаны к определенному объекту, то их можно вызывать напрямую через имя класса и оператор разрешения области видимости, а также через объекты класса (но это не рекомендуется).

Объявление и определение переменной.

В глобальном контексте переменная сначала требует объявления. Таким образом, компилятор будет знать её имя и тип. Определение переменной требует выделения под неё памяти и инициализации. Посмотрите следующий код. Он абсолютно легален и должен работать по стандарту

#include <conio.h> #include <stdio.h> int Global; //Объявили переменную int Global = 20; //Определили переменную void main() { printf(«%d», Global); getch(); }

Теперь, что будет, если одновременно объявить переменную и инициализировать её. Это определение переменной, которое требует её объявления

int Global = 20;

Следующая программа не скомпилируется

#include <conio.h> #include <stdio.h> extern int Global; void main() { Global = 30; printf(«%d», Global); getch(); }

Это связано с тем, что отсутствует определение переменной. Если определить переменную внутри main, то это будет уже другой экземпляр переменной, которая будет расположена на стеке. Вообще, при работе с одним файлом использование extern переменных не оправдано. Рассмотрим ситуацию, когда у нас имеются ещё два файла – заголовочный File1.h и File1.c. В заголовочном файле объявим extern переменную Global

#ifndef _FILE1_H_ #define _FILE1_H_ extern int Global; #endif

в файле исходного кода определим её

#include «File1.h» int Global = 100;

После подключения файла File1.h можно использовать эту переменную в файле main.c, при этом гарантировано, что существует только один экземпляр этой переменной для всех файлов проекта

#include <conio.h> #include <stdio.h> #include «File1.h» void main() { printf(«%d\n», Global); getch(); }

Если теперь определим функцию, которая изменяет эту переменную, то все функции из всех файлов будут видеть эти изменения.

#ifndef _FILE1_H_ #define _FILE1_H_ #include <stdio.h> extern int Global; void changeAndPrint(); #endif #include «File1.h» int Global = 100; void changeAndPrint() { printf(«from File1: Global = %d\n», Global); Global = 1234; printf(«changed to %d\n», Global); } #include <conio.h> #include <stdio.h> #include «File1.h» void main() { Global = 567; printf(«From main: Global = %d\n», Global); changeAndPrint(); printf(«From main: Global = %d\n», Global); getch(); }

Вывод

Итоговый наследник message_holder_t

Теперь можно посмотреть на то, что же из себя представляет , для реализации которого потребовались все эти базовые классы и метафункции (из реализации удалена часть методов для конструирования экземпляра хранящегося в message_holder-е сообщения):

По сути все то, что мы разбирали выше, потребовалось для того, чтобы записать вот этот «вызов» двух метафункций:

Т.к. это не первый вариант, а результат упрощения и сокращения кода, то могу сказать, что компактные формы метафункций ну очень сильно уменьшают объем кода и увеличивают его понятность (если о понятности здесь вообще уместно говорить).

А что было бы, если бы…

А вот если бы в C++ был настолько же мощен, как в D, то можно было бы написать что-то вроде:

Как по мне, так отличия слишком уж разительны. И они не в пользу текущего C++ 🙁
(разобранный выше C++ный код в виде одной сплошной «портянки» можно увидеть здесь).

Кстати говоря, я не очень сильно слежу за тем, что происходит в области предложений по метапрограммированию и рефлексии для будущих версий С++. Но из того, что помню, складывается ощущение, что предлагавшиеся Саттером метаклассы не очень упростят вот эту конкретную задачу. Как я понимаю, посредством метаклассов можно будет написать генератор классов . Может быть такой генератор получится и несложным в написании, но вряд ли такой подход в данном конкретном случае окажется выразительнее и понятнее, чем в случае действительно продвинутого .

Заключение

Как по мне, так этот пример показывает весь блеск и нищету C++. Да, можно сотворить все что угодно. В смысле, можно сделать шаблонный класс, содержимое которого будет кардинально меняться в зависимости от параметров шаблона.

Но вот чтобы сделать это придется несколько поломать мозги и написать на шаблонах столько вспомогательного кода, что копаться во всем этом не будет желания даже у автора.

Тем не менее, сам факт того, что на С++ можно такое сотворить, меня лично радует. Огорчает количество труда и объем кода, который для этого потребуется. Но, надеюсь, что со временем объем этого кода и его сложность будет только сокращаться. В принципе, это видно уже сейчас. Ибо для C++98/03 я даже не взялся бы такой трюк проделывать, тогда как начиная с C++11 делать подобное становится все проще и проще.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector