Всемогущий alias
Русский перевод моей статьи «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; // ошибка
Также можно объявлять псевдонимы для последовательностей типов и выражений — у меня есть отдельная статья об этом.