Русский перевод моей статьи «Almighty Alias». Оригинал опубликован на Medium.

alias — мое любимое ключевое слово в D! Не перестаю удивляться тому, сколько разных вещей оно делает. В D почти ко всему можно объявить псевдоним, и есть фичи, которые работают исключительно при помощи alias.

Простейший случай применения alias — это объявление альтернативного имени для типа или объекта. Это очень полезно, например, для сохранения обратной совместимости API при изменении имен:

alias OldType = NewType;

Вы можете объявить псевдоним со старым именем и пометить его атрибутом deprecated, чтобы код, использующий старый API, компилировался, но выводил предупреждение об устаревании:

deprecated("use NewType instead") alias OldType = NewType;

Другой распространенный случай — объявление коротких имен для лямбда-выражений:

alias Sqr = x => x * x;

writeln([2, 3, 4].map!Sqr);

Но, пожалуй, самое удивительное свойство alias — это то, что его можно использовать в параметрах шаблонов. Это стандартный способ передавать в шаблон значение вместо типа, чтобы использовать это значение в выражениях времени компиляции. В следующем примере значение x вычисляется во времени компиляции при помощи предоставленного пользователем числового литерала:

auto sqr(alias a)()
{
    enum sqrA = a * a;
    return sqrA;
}

enum x = sqr!10;
writeln(x);

Как насчет alias в шаблонном псевдониме? Это возможно — и довольно полезно в некоторых ситуациях!

alias Inc(alias a) = { a++; };

int x = 1;
Inc!x();
writeln(x);

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

Элегантный способ реализации compile-time ограничений — шаблонный enum с alias-параметром:

enum bool isClass(alias T) = is(T == class);

class MyClass
{
    int x;
}

assert(isClass!MyClass);

Старомодные шаблоны template из эпохи D1 до сих пор иногда используются, и alias — ваш лучший друг при работе с ними. Например, вот как можно объявить переменную при помощи такого шаблона:

template ArrayOfSquares(T, T initValue)
{
    T[10] array = initValue * initValue;
    alias ArrayOfSquares = array;
}

auto a = ArrayOfSquares!(int, 2);

Вернемся к alias-параметрам и еще немного повеселимся с ними. Возьмем шаблон, который автоматически инициализирует состояние параметром времени компиляции:

struct ArrayOfCubes(T, T initValue)
{
    T[10] array = initValue * initValue * initValue;
}

auto cubes = ArrayOfCubes!(int, 2)();

Теперь представьте, что вы хотите использовать ArrayOfCubes в функции, и вам нужно значение initValue (вы же не хотите вычислять кубический корень?). Это звучит тривиально, но нижеследующий код не скомпилируется:

// Ошибка!
void someFunc(T, T initValue)(ArrayOfCubes!(T, initValue) cubes)
{
    writeln(initValue);
}

На выручку приходит alias-параметр!

void someFunc(T, alias initValue)(ArrayOfCubes!(T, initValue) cubes)
{
    writeln(initValue);
}

И, наконец, alias используется в конструкции alias this, которая позволяет классу или структуре неявно отсылать к одному из ее полей:

struct Foo
{
    int x;
    alias x this;
}

auto f = Foo(5);
writeln(f * 2); // то же самое, что f.x * 2

alias работает с символами, типами и выражениями времени компиляции. Выражения времени выполнения не поддерживают псевдонимы. Например, следующий код не скомпилируется:

int x = 8;
alias a = x + 1; // ошибка

Также можно объявлять псевдонимы для последовательностей типов и выражений — у меня есть отдельная статья об этом.

Оставить комментарий

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