std.typecons

Переместиться к: alignForSize · AutoImplement · BitFlags · BlackHole · Flag · generateAssertTrap · generateEmptyFunction · isBitFlagEnum · isTuple · No · Nullable · NullableRef · Proxy · Rebindable · rebindable · RefCounted · refCounted · RefCountedAutoInitialize · ReplaceType · reverse · scoped · Ternary · Tuple · tuple · Typedef · TypedefType · Unique · UnqualRef · unwrap · WhiteHole · wrap · Yes

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

Исходный код: std/typecons.d

Краткий обзор:

// value tuples
alias Coord = Tuple!(float, "x", float, "y", float, "z");
Coord c;
c[1] = 1;       // access by index
c.z = 1;        // access by given name
alias DicEntry = Tuple!(string, string); // names can be omitted

// Rebindable references to const and immutable objects
void bar()
{
    const w1 = new Widget, w2 = new Widget;
    w1.foo();
    // w1 = w2 would not work; can't rebind const object
    auto r = Rebindable!(const Widget)(w1);
    // invoke method as if r were a Widget object
    r.foo();
    // rebind r to refer to another object
    r = w2;
}

Лицензия:
Boost License 1.0.
Авторы:
Andrei Alexandrescu, Bartosz Milewski, Don Clugston, Shin Fujishiro, Kenji Hara

Переместиться к: create · isEmpty · opAssign · RefT · release · this

struct Unique(T);
Инкапсулирует уникальное владение ресурсом. Ресурс типа T удаляется в конце области видимости, если только он не передаётся. Передача может быть явной, путем вызова release или неявной, когда Unique возвращается из функции. Ресурс может быть полиморфным объектом класса, и в этом случае Unique также ведет себя полиморфно.
Примеры:
static struct S
{
    int i;
    this(int i){this.i = i;}
}
Unique!S produce()
{
    // Строим уникальный экземпляр S в куче 
    Unique!S ut = new S(5);
    // Неявная передача владения
    return ut;
}
// Заимствование уникального ресурса по ссылке 
void increment(ref Unique!S ur)
{
    ur.i++;
}
void consume(Unique!S u2) // Потребление 
{
    assert(u2.i == 6);
    // Здесь ресурс автоматически удаляется 
}
Unique!S u1;
assert(u1.isEmpty);
u1 = produce();
increment(u1);
assert(u1.i == 6);
//consume(u1); // Ошибка: u1 не является копируемым
// Передача владения ресурсом
consume(u1.release);
assert(u1.isEmpty);
alias RefT = T;
Представляет ссылку на T. Разрешает T*, если T является типом значения.
Unique!T create(A...)(auto ref A args)
if (__traits(compiles, new T(args)));
Позволяет безопасное построение Unique. Создаёт ресурс и гарантирует уникальное владение им (если T не публикует псевдонимы на this).

Замечание: Вложенные структуры/классы не могут быть созданы.

Параметры:
A args Аргументы, передаваемые в конструктор T.
static class C {}
auto u = Unique!(C).create();

Переместиться к: 2 · 3

this(RefT p);
Конструктор, который принимает r-значение. Он обеспечит уникальность, если r-значение не является просто представлением о l-значении (например, cast). Типичное использование:
Unique!Foo f = new Foo;
this(ref RefT p);
Конструктор, который принимает l-значение. Он обнуляет его источник. Обнуление гарантирует уникальность в случае, если нет предыдущих псевдонимов к источнику.
this(U)(Unique!U u)
if (is(u.RefT : RefT));
Конструктор, который принимает Unique типа, который можно преобразовать в наш тип.
Обычно используется для передачи Unique r-значения производного типа в Unique базового типа.

Пример:

class C : Object {}

Unique!C uc = new C;
Unique!Object uo = uc.release;

void opAssign(U)(Unique!U u)
if (is(u.RefT : RefT));
Передаёт владение с Unique типа, который возможно преобразовать в наш тип.
const @property bool isEmpty();
Возвращает, существует ли ресурс.
Unique release();
Передаёт владение на Unique r-значения. Обнуляет текущее содержимое.

Переместиться к: expand · fieldNames · opAssign · opCmp · opEquals · rename · slice · this · toHash · toString · Types

struct Tuple(Specs...);
Кортеж значений, например Tuple!(int, string) – это запись, в которой хранится int и string. Tuple может использоваться для объединения значений вместе, особенно при возврате нескольких значений из функции. Если obj является кортежем, отдельные члены доступны с помощью синтаксиса obj[0] для первого поля, obj[1] для второго и т. д.
Выбор индексации, начинающейся с нуля, вместо индексации, начинающейся с единицы, был мотивирован возможностью использования значений Tuple с различными циклическими конструкциями времени компиляции (например, итерация std.meta.AliasSeq), в которых повсеместно используется индексирование, начинающееся с нуля.
Параметры:
Specs Список типов (и, необязательно, имен членов), которые содержит кортеж.
Примеры:
Tuple!(int, int) point;
// назначение координат
point[0] = 5;
point[1] = 6;
// чтение координат
auto x = point[0];
auto y = point[1];
Примеры:
Члены Tuple можно именовать. Допустимо смешивать именованные и безымянные члены. Доступ по индексу по-прежнему применим ко всем полям.
alias Entry = Tuple!(int, "index", string, "value");
Entry e;
e.index = 4;
e.value = "Hello";
assert(e[1] == "Hello");
assert(e[0] == 4);
Примеры:
Tuple с именованными полями представляет собой отдельный тип по сравнению с Tuple с неименованными полями, то есть присвоение каждого имени даст отдельный тип для Tuple. Два кортежа, различающиеся именами, являются различными, хотя у них может быть одинаковая структура.
Tuple!(int, "x", int, "y") point1;
Tuple!(int, int) point2;
assert(!is(typeof(point1) == typeof(point2)));
alias Types = staticMap!(extractType, fieldSpecs);
Типы компонентов Tuple.
Примеры:
alias Fields = Tuple!(int, "id", string, float);
static assert(is(Fields.Types == AliasSeq!(int, string, float)));
alias fieldNames = staticMap!(extractName, fieldSpecs);
Имена компонентов Tuple. Для безымянных полей возвращаются пустые имена.
Примеры:
alias Fields = Tuple!(int, "id", string, float);
static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
Types expand;
Используйте t.expand для Tuple t, чтобы развернуть его в списко компонентов. Результат expand действует так, как если бы компоненты кортежа были перечислены в виде списка значений. (Без этого, Tuple является единичным значением.)
Примеры:
auto t1 = tuple(1, " hello ", 2.3);
assert(t1.toString() == `Tuple!(int, string, double)(1, " hello ", 2.3)`);

void takeSeveralTypes(int n, string s, bool b)
{
    assert(n == 4 && s == "test" && b == false);
}

auto t2 = tuple(4, "test", false);
//t.expand действует как список значений
takeSeveralTypes(t2.expand);

Переместиться к: 2 · 3

this(Types values);
Конструктор, принимающий по одному значению для каждого поля.
Параметры:
Types values Список значений, имеющих те же типы, что и заданные в поле Types этого кортежа, или типы, которые могут быть неявно преобразованы в эти типы. Они должны быть расположены в том же порядке, что и в Types.
Примеры:
alias ISD = Tuple!(int, string, double);
auto tup = ISD(1, "test", 3.2);
assert(tup.toString() == `Tuple!(int, string, double)(1, "test", 3.2)`);
this(U, size_t n)(U[n] values)
if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types));
Конструктор, принимающий совместимый массив.
Параметры:
U[n] values Совместимый статический массив для создания из него кортежа. Срезы массива не поддерживаются.
Примеры:
int[2] ints;
Tuple!(int, int) t = ints;
this(U)(U another)
if (areBuildCompatibleTuples!(typeof(this), U));
Конструктор, принимающий совместимый кортеж. Два кортежа совместимы, если они имеют одну и ту же длину, и для каждого типа T в левой части соответствующий тип U в правой части может быть неявно преобразован в T.
Параметры:
U another Совместимый Tuple для сборки. Его тип должен быть совместим с типом целевого Tuple.
Примеры:
alias IntVec = Tuple!(int, int, int);
alias DubVec = Tuple!(double, double, double);

IntVec iv = tuple(1, 1, 1);

//Хорошо, int может быть неявно преобразован в double
DubVec dv = iv;
//Ошибка: double не может быть неявно преобразован в int
//IntVec iv2 = dv;
bool opEquals(R)(R rhs)
if (areCompatibleTuples!(typeof(this), R, "=="));

const bool opEquals(R)(R rhs)
if (areCompatibleTuples!(typeof(this), R, "=="));
Сравнение на равенство. Два кортежа считаются равными только в том случае, если они отвечают следующим критериям:
  • Кортежи имеют одинаковую длину.
  • Для каждого типа T в левой части и каждого типа U в правой части, значения типа T можно сравнивать со значениями типа U.
  • Для каждого значения v1 в левой части и каждого значения v2 в правой части, выражение v1 == v2 истинно.
Параметры:
R rhs Tuple для сравнения. Он должен соответствовать критериям сравнения между кортежами.
Возвращает:
true, если кортежи равны, в противном случае false.
Примеры:
Tuple!(int, string) t1 = tuple(1, "test");
Tuple!(double, string) t2 =  tuple(1.0, "test");
//Хорошо, int можно сравнивать с double
//оба имеют значение 1
assert(t1 == t2);
int opCmp(R)(R rhs)
if (areCompatibleTuples!(typeof(this), R, "<"));

const int opCmp(R)(R rhs)
if (areCompatibleTuples!(typeof(this), R, "<"));
Сравнение на упорядочение.
Параметры:
R rhs Tuple для сравнения. Он должен соответствовать критериям сравнения между кортежами.
Возвращает:
Для любых значений v1 в правой части и v2 в левой части:
  • Отрицательное целое число, если выражение v1 < v2 истинно.
  • Положительное целое число, если выражение v1 > v2 истинно.
  • 0, если выражение v1 == v2 истинно.
Примеры:
Первое значение v1, для которого v1 > v2 истинно, определяет результат. Это может привести к неожиданному поведению.
auto tup1 = tuple(1, 1, 1);
auto tup2 = tuple(1, 100, 100);
assert(tup1 < tup2);

//Для сравнения имеет значение только первый результат
tup1[0] = 2;
assert(tup1 > tup2);
void opAssign(R)(auto ref R rhs)
if (areCompatibleTuples!(typeof(this), R, "="));
Присвоение от другого Tuple.
Параметры:
R rhs Исходный кортеж. Каждый элемент исходного кортежа должен неявно приводиться к каждому соответствующему элементу целевого кортежа.

Переместиться к: 2

ref return auto rename(names...)()
if (names.length == 0 || allSatisfy!(isSomeString, typeof(names)));
Переименовывает элементы кортежа Tuple.
rename использует переданные имена names и возвращает новый Tuple, используя эти имена, при этом содержимое остаётся неизменным. Если передано меньше имён, чем членов в кортеже, то оставшиеся в конце члены не изменяются. Пустая строка удаляет имя для соответствующего элемента. При передаче большего количества имён, чем количество присутствующих в кортеже членов, вызывается ошибка времени компиляции.
Примеры:
auto t0 = tuple(4, "hello");

auto t0Named = t0.rename!("val", "tag");
assert(t0Named.val == 4);
assert(t0Named.tag == "hello");

Tuple!(float, "dat", size_t[2], "pos") t1;
t1.pos = [2, 1];
auto t1Named = t1.rename!"height";
t1Named.height = 3.4f;
assert(t1Named.height == 3.4f);
assert(t1Named.pos == [2, 1]);
t1Named.rename!"altitude".altitude = 5;
assert(t1Named.height == 5);

Tuple!(int, "a", int, int, "c") t2;
t2 = tuple(3,4,5);
auto t2Named = t2.rename!("", "b");
// "a" больше не является именем
static assert(!hasMember!(typeof(t2Named), "a"));
assert(t2Named[0] == 3);
assert(t2Named.b == 4);
assert(t2Named.c == 5);

// не разрешено указывать больше имён, чем количество членов кортежа
static assert(!__traits(compiles, t2.rename!("a","b","c","d")));

// использование в диапазонном pipeline
import std.range : iota, zip;
import std.algorithm.iteration : map, sum;
auto res = zip(iota(1, 4), iota(10, 13))
    .map!(t => t.rename!("a", "b"))
    .map!(t => t.a * t.b)
    .sum;
assert(res == 68);
ref auto rename(alias translate)()
if (is(typeof(translate) : V[K], V, K) && isSomeString!V && (isSomeString!K || is(K : size_t)));
Перегрузка rename, которая принимает ассоциативный массив translate в качестве параметра шаблона, где ключи являются либо именами, либо индексами членов, подлежащих изменению, а новые имена – соответствующими значениями. Каждый ключ в translate должен быть именем члена кортежа. Для пустых строк применяются те же правила, что и для перегрузки rename с переменным числом аргументов.
Примеры:
//замена имён по их текущему имени

Tuple!(float, "dat", size_t[2], "pos") t1;
t1.pos = [2, 1];
auto t1Named = t1.rename!(["dat": "height"]);
t1Named.height = 3.4;
assert(t1Named.pos == [2, 1]);
t1Named.rename!(["height": "altitude"]).altitude = 5;
assert(t1Named.height == 5);

Tuple!(int, "a", int, "b") t2;
t2 = tuple(3, 4);
auto t2Named = t2.rename!(["a": "b", "b": "c"]);
assert(t2Named.b == 3);
assert(t2Named.c == 4);
Примеры:
//замена имён по их позиции

Tuple!(float, "dat", size_t[2], "pos") t1;
t1.pos = [2, 1];
auto t1Named = t1.rename!([0: "height"]);
t1Named.height = 3.4;
assert(t1Named.pos == [2, 1]);
t1Named.rename!([0: "altitude"]).altitude = 5;
assert(t1Named.height == 5);

Tuple!(int, "a", int, "b", int, "c") t2;
t2 = tuple(3, 4, 5);
auto t2Named = t2.rename!([0: "c", 2: "a"]);
assert(t2Named.a == 5);
assert(t2Named.b == 4);
assert(t2Named.c == 3);
@property ref @trusted Tuple!(sliceSpecs!(from, to)) slice(size_t from, size_t to)()
if (from <= to && to <= Types.length);
Получение среза кортежа.
Параметры:
from значение типа size_t, обозначающее начальную позицию среза.
to значение типа size_t, определяющее конечную позицию среза (не включая её).
Возвращает:
Новый кортеж Tuple, который представляет собой срез [from, to) от исходного. Он имеет те же типы и значения, что и диапазон [from, to) в исходном кортеже.
Примеры:
Tuple!(int, string, float, double) a;
a[1] = "abc";
a[2] = 4.5;
auto s = a.slice!(1, 3);
static assert(is(typeof(s) == Tuple!(string, float)));
assert(s[0] == "abc" && s[1] == 4.5);
const nothrow @trusted size_t toHash();
Создаёт хэш этого кортежа.
Возвращает:
Значение типа size_t, представляющее хэш этого кортежа Tuple.

Переместиться к: toString

template toString()
const string toString()();
Преобразование в строку.
Возвращает:
Строковое представление этого кортежа Tuple.

Переместиться к: toString

const void toString(DG)(scope DG sink);

const void toString(DG, Char)(scope DG sink, FormatSpec!Char fmt);
Форматирует Tuple с помощью %s, %(inner%) или %(inner%|sep%).
Форматы, поддерживаемые Tuple
ФорматОписание

%s

Формат в виде Tuple!(типы)(элементы, каждый из которых отформатирован с помощью %s).

%(inner%)

Формат inner применяется к раскрытому кортежу, поэтому он может содержать столько форматов, сколько полей содержится в Tuple.

%(inner%|sep%)

Формат inner – это один формат, который применяется ко всем полям кортежа. Этот формат должен быть совместим со всеми из них. sep – это формат разделителя.

 Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];

 // Формат по-умолчанию
 assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`);

 // По одному формату для каждого отдельного компонента
 assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10))         == `0x1 v 1.0000 w 0xa`);
 assert(format(  "%#x v %.4f w %#x"  , tuple(1, 1.0, 10).expand)  == `0x1 v 1.0000 w 0xa`);

 // Один формат для всех компонентов
 assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`);

 // Массив кортежей
 assert(format("%(%(f(%d) = %.1f%);  %)", tupList) == `f(1) = 1.0;  f(2) = 4.0;  f(3) = 9.0`);


 // Ошибка: пропущено %( %) .
 assertThrown!FormatException(
     format("%d, %f", tuple(1, 2.0)) == `1, 2.0`
 );

 // Ошибка: пропущено %( %| %) .
 assertThrown!FormatException(
     format("%d", tuple(1, 2)) == `1, 2`
 );

 // Ошибка: %d неадекватно для double. 
 assertThrown!FormatException(
     format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0`
 );
ReverseTupleType!T reverse(T)(T t)
if (isTuple!T);
Создаёт копию кортежа, в которой поля расположены в обратном порядке.
Параметры:
T t Копируемый кортеж Tuple.
Возвращает:
Копию t с полями в обратном порядке.
Примеры:
auto tup = tuple(1, "2");
assert(tup.reverse == tuple("2", 1));
template tuple(Names...)
Создает экземпляр объекта Tuple, инициализируемый в соответствии с данными аргументами.
Параметры:
Names Список строк, последовательно именующих каждое из полей кортежа. Каждое имя относится к соответствующему полю, переданному в Args. Имя не обязательно указывать для каждого поля, но так как имена должны быть расположены по порядку, невозможно пропустить одно поле и именовать следующее после него.
Args args Значения для инициализации кортежа. Тип Tuple будет выведен из типов заданных значений.
Возвращает:
Новый кортеж Tuple, тип которого выводится из приведенных аргументов.
Примеры:
auto value = tuple(5, 6.7, "hello");
assert(value[0] == 5);
assert(value[1] == 6.7);
assert(value[2] == "hello");

// Можно указывать имена полей.
auto entry = tuple!("index", "value")(4, "Hello");
assert(entry.index == 4);
assert(entry.value == "Hello");
enum auto isTuple(T);
Возвращает true, если и только если T является экземпляром std.typecons.Tuple.
Параметры:
T The type to check.
Возвращает:
true, если T является экземпляром типа Tuple, false в противном случае.
Примеры:
static assert(isTuple!(Tuple!()));
static assert(isTuple!(Tuple!(int)));
static assert(isTuple!(Tuple!(int, real, string)));
static assert(isTuple!(Tuple!(int, "x", real, "y")));
static assert(isTuple!(Tuple!(int, Tuple!(real), string)));
template Rebindable(T) if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
Rebindable!(T) – это простая, эффективная обёртка, которая ведет себя точно так же, как объект типа T, за исключением того, что вы можете переназначить её, чтобы она ссылалась на другой объект. Для полноты Rebindable!(T) создаёт псевдоним самой себя на T, если T не является константным типом объекта.
Вы можете использовать Rebindable, если вы хотите иметь изменяемое хранилище, ссылающееся на const-объекты, например массив ссылок, которые необходимо отсортировать на месте. Rebindable не нарушает надёжность системы типов D и не вносит никаких рисков, обычно связанных с cast.
Параметры:
T Объект, интерфейс, срез массива или ассоциативный массив.
Примеры:
Нельзя переназначить обычные const-ссылки на объекты.
class Widget { int x; int y() const { return x; } }
const a = new Widget;
// Всё хорошо
a.y();
// ошибка! нельзя модифицировать const a
// a.x = 5;
// ошибка! нельзя модифицировать const a
// a = new Widget;
Примеры:
Тем не менее, обёртку Rebindable!(Widget) позволяется переназначать, в то время как во всех остальных случаях она ведет себя точно так же, как const Widget.
class Widget { int x; int y() const { return x; } }
auto a = Rebindable!(const Widget)(new Widget);
// Всё хорошо
a.y();
// ошибка! нельзя модифицировать const a
// a.x = 5;
// Всё хорошо
a = new Widget;

Переместиться к: 2

Rebindable!T rebindable(T)(T obj)
if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T);
Удобная функция для создания Rebindable, использующая автоматический вывод типа.
Параметры:
T obj Ссылка на объект, интерфейс, ассоциативный массив или срез массива для инициализации Rebindable.
Возвращает:
Новую сконструированную обёртку Rebindable, инициализированную данной ссылкой.
Rebindable!T rebindable(T)(Rebindable!T obj);
Эта функция просто возвращает передаваемый объект Rebindable. Она полезна в случаях обобщённого программирования, когда передаваемый объект может оказаться либо обычным классом, либо обёрткой Rebindable.
Параметры:
Rebindable!T obj Экземпляр Rebindable!T.
Возвращает:
obj без каких-либо модификаций.
template UnqualRef(T) if (is(T == class) || is(T == interface))
Похоже на Rebindable!(T), но снимает все квалификаторы с ссылки, а не только константность/ неизменность. В основном предполагается использовать с shared (ссылку с классом данных, общим для всех потоков, сделать принадлежащей локальному потоку)
Параметры:
T Тип класса или интерфейса.
Примеры:
class Data {}

static shared(Data) a;
static UnqualRef!(shared Data) b;

import core.thread;

auto thread = new core.thread.Thread({
    a = new shared Data();
    b = new shared Data();
});

thread.start();
thread.join();

assert(a !is null);
assert(b is null);
string alignForSize(E...)(const char[][] names...);
Упорядочивает передаваемые члены, чтобы свести к минимуму размер при сохранении выравнивания. Выравнивание не всегда оптимально для 80-битных типов real, или для структур, объявленных как align(1).
Параметры:
E Список типов для выравнивания, представляющих поля агрегата, такого как структура или класс.
char[][] names Имена полей, которые нужно выровнять.
Возвращает:
Строку, примешиваемую к агрегату, такому как struct или class
Примеры:
struct Banner {
    mixin(alignForSize!(byte[6], double)(["name", "height"]));
}

Переместиться к: 2 · get · isNull · nullify · opAssign · this

struct Nullable(T);
Определяет значение, соединённое с отличительным «нулевым» состоянием, которое обозначает отсутствие значения. При построении по умолчанию, объект Nullable!T инициализируется состоянием null. Присвоение делает его не-нулевым. Вызов nullify может снова занулить его.
Фактически Nullable!T содержит T и bool.
Примеры:
struct CustomerRecord
{
    string name;
    string address;
    int customerNum;
}

Nullable!CustomerRecord getByName(string name)
{
    //A bunch of hairy stuff

    return Nullable!CustomerRecord.init;
}

auto queryResult = getByName("Doe, John");
if (!queryResult.isNull)
{
    //Процесс регистрации клиента г-на Doe
    auto address = queryResult.address;
    auto customerNum = queryResult.customerNum;

    //Что-то сделать с этой информацией о клиенте
}
else
{
    //Добавить клиента в базу данных
}

Переместиться к: 2

inout this(inout T value);
Конструктор, инициализирующий this значением value.
Параметры:
T value Инициализирующее значение

Переместиться к: 2

const pure nothrow @property @safe bool isNull();
Проверяет, находится ли этот объект в нулевом состоянии.
Возвращает:
true если и только если this находится в состоянии null, в противном случае false.
Примеры:
Nullable!int ni;
assert(ni.isNull);

ni = 0;
assert(!ni.isNull);

Переместиться к: 2

void nullify()();
Переводит этот объект в состояние null.
Примеры:
Nullable!int ni = 0;
assert(!ni.isNull);

ni.nullify();
assert(ni.isNull);

Переместиться к: 2

void opAssign()(T value);
Присваивает значение value внутреннему состоянию. Если присваивание выполнено успешно, this становится ненулевым.
Параметры:
T value Значение типа T для присвоения этому объекту Nullable.
Примеры:
Если этот объект Nullable обёртывает тип, который уже может иметь значение null (например, указатель), то присвоение значения null этому Nullable ничем не отличается от назначения любого другого значения типа T, и полученный код будет очень странным. Настоятельно рекомендуется избегать этого, вместо этого используя версию Nullable, которая принимает дополнительный аргумент шаблона nullValue.
//Проходит
Nullable!(int*) npi;
assert(npi.isNull);

//Проходит?!
npi = null;
assert(!npi.isNull);

Переместиться к: 2

inout pure nothrow @property ref @safe inout(T) get();
Возвращает значение. this не должно быть в состоянии null. Эта функция также вызывается при неявном преобразования в T.
Возвращает:
Значение, содержащееся внутри этого Nullable.
Примеры:
import std.exception : assertThrown, assertNotThrown;

Nullable!int ni;
//`get` вызывается неявно. Бросает
//AssertError в не-релизном режиме
assertThrown!Throwable(ni == 0);

ni = 0;
assertNotThrown!Throwable(ni == 0);
struct Nullable(T, T nullValue);
То же самое, что и Nullable!T, за исключением того, что нулевое состояние определено через конкретное значение. Например, Nullable!(uint, uint.max) является типом uint, который выделяет значение uint.max для обозначения нулевого состояния. Nullable!(T, nullValue) более эффективен с точки зрения использования памяти, чем Nullable!T, потому что ему не нужно хранить дополнительный bool.
Параметры:
T Оборачиваемый тип, для которого Nullable предоставляет нулевое значение.
nullValue Нулевое значение, которое будет обозначать нулевое состояние этого Nullable. Должно быть типа T.
Примеры:
Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle)
{
    //Ищет needle, возвращая -1, если не найдено 

    return Nullable!(size_t, size_t.max).init;
}

void sendLunchInvite(string name)
{
}

//Это безопаснее, чем C...
auto coworkers = ["Jane", "Jim", "Marry", "Fred"];
auto pos = indexOf(coworkers, "Bob");
if (!pos.isNull)
{
    //Отправить Бобу приглашение на обед 
    sendLunchInvite(coworkers[pos]);
}
else
{
    //Боб не найден; сообщить об ошибке
}

//И нет накладных расходов
static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof);
this(T value);
Конструктор, инициализирующий this значением value.
Параметры:
T value Инициализирующее значение
const @property bool isNull();
Проверяет, находится ли this в состоянии null.
Возвращает:
true если и только если this находится в состоянии null, в противном случае false.
Примеры:
Nullable!(int, -1) ni;
//Инициализация в состояние "null"
assert(ni.isNull);

ni = 0;
assert(!ni.isNull);
void nullify()();
Переводит этот объект в состояние null.
Примеры:
Nullable!(int, -1) ni = 0;
assert(!ni.isNull);

ni = -1;
assert(ni.isNull);
void opAssign()(T value);
Присваивает значение value внутреннему состоянию. Если присваивание выполнено успешно, this становится ненулевым. Никаких проверок на null не производится. Обратите внимание, что приваивание может оставить объект в состоянии null.
Параметры:
T value Значение типа T для присвоения этому Nullable. Если оно равно nullvalue, тогда внутреннее состояние этого Nullable будет установлено в null.
Примеры:
Если этот Nullable обёртывает тип, который уже может иметь значение null (например, указатель), и это значение не задано для nullValue, то присвоение значения null этому Nullable ничем не отличается от назначения любого другого значения типа T и получившийся код будет выглядеть очень странно. Настоятельно рекомендуется избегать этого, используя «встроенное» для типа T значение null для nullValue.
//Проходит
enum nullVal = cast(int*)0xCAFEBABE;
Nullable!(int*, nullVal) npi;
assert(npi.isNull);

//Проходит?!
npi = null;
assert(!npi.isNull);
inout @property ref inout(T) get();
Возвращает значение. this не должно быть в состоянии null. Эта функция также вызывается при неявном преобразования в T.
Возвращает:
Значение, содержащееся внутри этого Nullable.
Примеры:
import std.exception : assertThrown, assertNotThrown;

Nullable!(int, -1) ni;
//`get` вызывается неявно. Бросает
//AssertError в не-релизном режиме
assertThrown!Throwable(ni == 0);

ni = 0;
assertNotThrown!Throwable(ni == 0);

Переместиться к: bind · get · isNull · nullify · opAssign · this

struct NullableRef(T);
Тоже, что и Nullable!T, за исключением того, что объект ссылается на значение, находящееся в другом месте в памяти. Это приводит к тому, что присваивания перезаписывают первоначально присвоенное значение. Внутренне NullableRef!T хранит только указатель на T (т.е. Nullable!T.sizeof == (T*).sizeof).
pure nothrow @safe this(T* value);
Конмтруктор, связывающий this со значением value.
Параметры:
T* value Привязываемое значение.
pure nothrow @safe void bind(T* value);
Привязывает внутреннее состояние к значению.
Параметры:
T* value Указатель на значение типа T, которое будет привязано к этому объекту NullableRef.
Примеры:
NullableRef!int nr = new int(42);
assert(nr == 42);

int* n = new int(1);
nr.bind(n);
assert(nr == 1);
const pure nothrow @property @safe bool isNull();
Возвращает true если и только если этот объект находится в состоянии null.
Возвращает:
true, если this находится в состоянии null, в противном случае false.
Примеры:
NullableRef!int nr;
assert(nr.isNull);

int* n = new int(42);
nr.bind(n);
assert(!nr.isNull && nr == 42);
pure nothrow @safe void nullify();
Переводит этот объект в состояние null.
Примеры:
NullableRef!int nr = new int(42);
assert(!nr.isNull);

nr.nullify();
assert(nr.isNull);
void opAssign()(T value)
if (isAssignable!T);
Присваивает значение value внутреннему состоянию.
Параметры:
T value Значение типа T для присвоения этому объекту NullableRef. Если внутреннее состояние этого NullableRef не было инициализировано, будет вызвана ошибка в не-релизном режиме.
Примеры:
import std.exception : assertThrown, assertNotThrown;

NullableRef!int nr;
assert(nr.isNull);
assertThrown!Throwable(nr = 42);

nr.bind(new int(0));
assert(!nr.isNull);
assertNotThrown!Throwable(nr = 42);
assert(nr == 42);
inout pure nothrow @property ref @safe inout(T) get();
Возвращает значение. this не должно быть в состоянии null. Эта функция также вызывается для неявного преобразования в T.
Примеры:
import std.exception : assertThrown, assertNotThrown;

NullableRef!int nr;
//`get` вызывается неявно. Будет брошена
//ошибка в не-релизном режиме
assertThrown!Throwable(nr == 0);

nr.bind(new int(0));
assertNotThrown!Throwable(nr == 0);
template BlackHole(Base)
Слово "BlackHole" переводится как "Чёрная дыра" – прим. пер.
BlackHole!Base является подклассом Base, который автоматически реализует все абстрактные функции-члены в Base как ничего не делающие функции. Каждая автоматически реализованная функция просто возвращает значение по умолчанию возвращаемого типа, не делая ничего.
Имя пришло из перловского модуля Class::BlackHole, созданного Sean M. Burke.
Параметры:
Base Не-финальный класс, от которого будет унаследован BlackHole.
Смотрите также:
Примеры:
import std.math : isNaN;

static abstract class C
{
    int m_value;
    this(int v) { m_value = v; }
    int value() @property { return m_value; }

    abstract real realValue() @property;
    abstract void doSomething();
}

auto c = new BlackHole!C(42);
assert(c.value == 42);

// Возвращает real.init, которое является NaN
assert(c.realValue.isNaN);
// Абстрактные функции реализованы как ничего не делающие
c.doSomething();
template WhiteHole(Base)
Слово "WhiteHole" переводится как "Белая дыра" – прим. пер.
WhiteHole!Base – это подкласс Base, который автоматически реализует все абстрактные функции-члены как функции, которые всегда терпят неудачу. Эти функции просто вызывают ошибку и никогда не возвращаются. Whitehole полезен для отлова использования функций-членов класса, которые не были реализованы.
Имя пришло из перловского модуля Class::WhiteHole, созданного Michael G Schwern.
Параметры:
Base Не-финальный класс, от которого будет унаследован WhiteHole.
Смотрите также:
Примеры:
import std.exception : assertThrown;

static class C
{
    abstract void notYetImplemented();
}

auto c = new WhiteHole!C;
assertThrown!NotImplementedError(c.notYetImplemented()); // вызывается ошибка
class AutoImplement(Base, alias how, alias what = isAbstractFunction): Base;
AutoImplement автоматически реализует (по умолчанию) все абстрактные функции-члены в классе или интерфейсе Base определённым образом.
Параметры:
how шаблон, который определяет, как функции будут реализованы/переопределены.
Два аргумента передаются в how: тип Base и псевдоним на реализованную функцию. Затем how должна вернуть реализованное тело функции в виде строки.
Сгенерированное тело функции может использовать эти ключевые слова:
  • a0, a1, …: аргументы, передаваемые в функцию;
  • args: кортеж аргументов;
  • self: псевдоним самой этой функции;
  • parent: псевдоним переопределяемой функции (если есть).
Вы можете использовать шаблонные функции-свойства (вместо неявных свойств шаблонов) для создания сложных функций: You may want to use templated property functions (instead of Implicit Template Properties) to generate complex functions:
// Печатает сообщения в журнал при каждом вызове переопределяемых функций.
string generateLogger(C, alias fun)() @property
{
    import std.traits;
    enum qname = C.stringof ~ "." ~ __traits(identifier, fun);
    string stmt;

    stmt ~= q{ struct Importer { import std.stdio; } };
    stmt ~= `Importer.writeln("Log: ` ~ qname ~ `(", args, ")");`;
    static if (!__traits(isAbstractFunction, fun))
    {
        static if (is(ReturnType!fun == void))
            stmt ~= q{ parent(args); };
        else
            stmt ~= q{
                auto r = parent(args);
                Importer.writeln("--> ", r);
                return r;
            };
    }
    return stmt;
}
what шаблон, который определяет, какие функции должны быть реализованы/переопределены.
Аргумент, передаваемый в what: псевдоним не финальной функции-члена в Base. Затем what должна вернуть логическое значение. Возврат true указывает, что переданная функция должна быть реализована /переопределена.
// Смотрит, если fun что-то возвращает.
enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void);

Замечание: Сгенерированный код вставляется в область видимости модуля std.typecons. Таким образом, любые полезные функции вне std.typecons не могут использоваться в сгенерированном коде. Чтобы обойти эту проблему, вы можете импортировать всё необходимое в локальную структуру, как это сделано в шаблоне generateLogger() в приведенном выше примере.

Недостатки:
  • Variadic-аргументы (аргументы с переменным их количеством – прим.пер.) в конструкторах не перенаправляются в super.
  • Глубокое наследование интерфейса вызывает ошибку компиляции с такими сообщениями, как "Error: function std.typecons.AutoImplement!(Foo).AutoImplement.bar does not override any function". [Bugzilla 2525, Bugzilla 3525]
  • Ключевое слово parent фактически является делегатом соответствующей функции-члена суперкласса. [Bugzilla 2540]
  • Использование псевдонима параметра шаблона в how и/или what может вызвать странную ошибку компиляции. Используйте шаблон кортежа параметра вместо него, чтобы обойти эту проблему. [Bugzilla 4217]
Честно говоря, какой-то мутный шаблон... Мало того, что у него крайне сомнительное назначение (зачем нужно создавать абстрактные методы, если потом их не реализовывать по-человечески?), так ещё и куча непонятных проблем (или ошибок?). Лично я им не планирую пользоваться. И в точности своего перевода здесь не уверен – прим.пер.
template generateEmptyFunction(C, func...)

enum string generateAssertTrap(C, func...);
Предопределенные how-политики для AutoImplement. Эти шаблоны также используются BlackHole и WhiteHole, соответственно.
template wrap(Targets...) if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))

template wrap(Targets...) if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))
Поддержка основанного на структурах безопасного преобразования типов.
Если источник имеет структурное соответствие с интерфейсом Targets, wrap создаёт внутренний обёртывающий класс, который наследует Targets и обёртывает объект-источник, а затем возвращает его.
template unwrap(Target) if (isMutable!Target)

template unwrap(Target) if (!isMutable!Target)
Извлекает объект, который завёрнут шаблоном wrap.
Примеры:
interface Quack
{
    int quack();
    @property int height();
}
interface Flyer
{
    @property int height();
}
class Duck : Quack
{
    int quack() { return 1; }
    @property int height() { return 10; }
}
class Human
{
    int quack() { return 2; }
    @property int height() { return 20; }
}

Duck d1 = new Duck();
Human h1 = new Human();

interface Refleshable
{
    int reflesh();
}
// не имеет структурного соответствия
static assert(!__traits(compiles, d1.wrap!Refleshable));
static assert(!__traits(compiles, h1.wrap!Refleshable));

// точное восходящее преобразование
Quack qd = d1.wrap!Quack;
assert(qd is d1);
assert(qd.quack() == 1);    // вызывается Duck.quack
// точное нисходящее преобразование
Duck d2 = qd.unwrap!Duck;
assert(d2 is d1);

// структурное восходящее преобразование
Quack qh = h1.wrap!Quack;
assert(qh.quack() == 2);    // вызывается Human.quack
// структурное нисходящее преобразование
Human h2 = qh.unwrap!Human;
assert(h2 is h1);

// структурное восходящее преобразование (два шага)
Quack qx = h1.wrap!Quack;   // Human -> Quack
Flyer fx = qx.wrap!Flyer;   // Quack -> Flyer
assert(fx.height == 20);    // вызывается Human.height
// структурное нисходящее преобразование (два шага)
Quack qy = fx.unwrap!Quack; // Flyer -> Quack
Human hy = qy.unwrap!Human; // Quack -> Human
assert(hy is h1);
// структурное нисходящее преобразование (один шаг)
Human hz = fx.unwrap!Human; // Flyer -> Human
assert(hz is h1);
Примеры:
interface A { int run(); }
interface B { int stop(); @property int status(); }
class X
{
    int run() { return 1; }
    int stop() { return 2; }
    @property int status() { return 3; }
}

auto x = new X();
auto ab = x.wrap!(A, B);
A a = ab;
B b = ab;
assert(a.run() == 1);
assert(b.stop() == 2);
assert(b.status == 3);
static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);

Переместиться к: no · yes

enum RefCountedAutoInitialize: int;
Параметры, касающиеся автоматической инициализации объекта RefCounted (см. определение RefCounted ниже).
no
Не выполнять автоматическую инициализацию объекта
yes
Выполнять автоматическую инициализацию объекта

Переместиться к: opAssign · refCountedPayload · RefCountedStore · refCountedStore · this

struct RefCounted(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes) if (!is(T == class) && !is(T == interface));
Определяет объект с подсчетом ссылок, содержащий значение T в качестве полезной нагрузки. RefCounted отслеживает все ссылки на объект, а когда счетчик ссылок падает до нуля, освобождает занимаемую память. RefCounted использует malloc и free для работы.
RefCounted небезопасен и должен использоваться с осторожностью. Никакие ссылки на внутренний объект не должны выходить за пределы объекта RefCounted.
Опция autoInit позволяет объекту гарантировать, что хранилище будет автоматически инициализировано. Удобно оставить autoInit == RefCountedAutoInitialize.yes (опция по умолчанию), но это имеет цену – проверка выполняется каждый раз при обращении к полезной нагрузке. Если autoInit == RefCountedAutoInitialize.no, то пользовательский код должен вызвать refCountedStore.isInitialized или refCountedStore.ensureInitialized, прежде чем пытаться получить доступ к полезной нагрузке. Это не приводит к разыменованию null-указателей.
Примеры:
// Пара $(D int) и $(D size_t) - последняя является  
// количеством ссылок, будет распределена динамически
auto rc1 = RefCounted!int(5);
assert(rc1 == 5);
// Нет дополнительного распределения, только прибавляется единица к счетчику ссылок
auto rc2 = rc1;
// Ссылочная семантика
rc2 = 42;
assert(rc1 == 42);
// память, занимаемая парой, освободится, когда rc1 и rc2 выйдут из области действия

Переместиться к: ensureInitialized · isInitialized · refCount

struct RefCountedStore;
Реализация хранилища RefCounted.
const pure nothrow @nogc @property @safe bool isInitialized();
Возвращает true тогда и только тогда, когда память под нижележащий объект была выделена и инициализирована.
const pure nothrow @nogc @property @safe size_t refCount();
Возвращает нижележащий счетчик ссылок, если он память под него распределена и инициализирована (положительное целое число), и 0 в противном случае.
void ensureInitialized();
Обеспечивает правильную инициализацию полезной нагрузки. Такой вызов обычно вставляется перед её использованием.
inout nothrow @property ref @safe inout(RefCountedStore) refCountedStore();
Возвращает структуру, реализующую хранилище.
this(A...)(auto ref A args)
if (A.length > 0);

this(T val);
Конструктор, который инициализирует полезную нагрузку.

Постусловие: refCountedStore.isInitialized

void opAssign(typeof(this) rhs);

void opAssign(T rhs);
Операторы присваивания
@property ref return T refCountedPayload();

inout pure nothrow @nogc @property ref return @safe inout(T) refCountedPayload();
Возвращает ссылку на полезную нагрузку. Если (autoInit == RefCountedAutoInitialize.yes), вызывается refCountedStore.ensureInitialized. В противном случае просто применяется assert(refCountedStore.isInitialized). Используется с псевдонимом alias refCountedPayload this;, поэтому вызывающие могут просто использовать объект RefCounted в качестве T.
Первая перегрузка существует только в том случае, если autoInit == RefCountedAutoInitialize.yes. Поэтому, если autoInit == RefCountedAutoInitialize.no или вызывается для объекта const или immutable, то refCountedPayload также будет квалифицирован как safe и nothrow (но все равно будет срабатывать assert в отсутствии инициализации).
RefCounted!(T, RefCountedAutoInitialize.no) refCounted(T)(T val);
Инициализирует RefCounted значением val. Шаблонный параметр T у RefCounted выводится из val. Эта функция может использоваться для перемещения не-копируемых значений в кучу. Она также отключает опцию autoInit у шаблона RefCounted.
Параметры:
T val Значение, для которого будет вестись подсчёт ссылок
Возвращает:
Инициализированный RefCounted, содержащий значение val.
Смотрите также:
Примеры:
static struct File
{
    string name;
    @disable this(this); // не-копируемый
    ~this() { name = null; }
}

auto file = File("name");
assert(file.name == "name");
// file не может быть скопирован и имеет уникального владельца
static assert(!__traits(compiles, {auto file2 = file;}));

// сделать RefCounted для file для совместного владения
import std.algorithm.mutation : move;
auto rcFile = refCounted(move(file));
assert(rcFile.name == "name");
assert(file.name == null);
auto rcFile2 = rcFile;
assert(rcFile.refCountedStore.refCount == 2);
// файл корректно закрывается при удалении последней ссылки
template Proxy(alias a)
Создает прокси для значения a, которое будет перенаправлять все операции при отключении неявных преобразований. Псевдоним a должен быть l-значением. Это полезно для создания нового типа из «базового» типа (хотя это не является отношением подтип-супертип, новый тип никак не связан с старым типом, по дизайну).
Новый тип поддерживает все операции, выполняемые базовым типом, включая все операторы, такие как +, --, <, [], и т.д.
Параметры:
a Значение, действующее как прокси для всех операций. Должно быть l-значением.
Примеры:
struct MyInt
{
    private int value;
    mixin Proxy!value;

    this(int n){ value = n; }
}

MyInt n = 10;

// Включает операции, которые имеет исходный тип.
++n;
assert(n == 11);
assert(n * 2 == 22);

void func(int n) { }

// Отключены неявные преобразования в исходный тип.
//int x = n;
//func(n);
Примеры:
Проксируемое значение должно быть l-значением.
struct NewIntType
{
    //Не работает; литерал '1' является
    //r-значением, не l-значением
    //mixin Proxy!1;

    //Хорошо, n - это l-значение
    int n;
    mixin Proxy!n;

    this(int n) { this.n = n; }
}

NewIntType nit = 0;
nit++;
assert(nit == 1);


struct NewObjectType
{
    Object obj;
    //Хорошо, obj - это l-значение
    mixin Proxy!obj;

    this (Object o) { obj = o; }
}

NewObjectType not = new Object();
assert(__traits(compiles, not.toHash()));
Примеры:
Существует одно исключение из того факта, что новый тип не связан со старым типом. Функции-псевдочлены можно использовать с новым типом; они будут перенаправлены на прокси-значение.
import std.math;

float f = 1.0;
assert(!f.isInfinity);

struct NewFloat
{
    float _;
    mixin Proxy!_;

    this(float f) { _ = f; }
}

NewFloat nf = 1.0f;
assert(!nf.isInfinity);
struct Typedef(T, T init = T.init, string cookie = null);
Typedef позволяет создать уникальный тип, основанный на существующем типе. В отличие от псевдонима alias, Typedef гарантирует, что два типа не считаются равными.

Пример:

alias MyInt = Typedef!int;
static void takeInt(int) { }
static void takeMyInt(MyInt) { }

int i;
takeInt(i);    // хорошо
takeMyInt(i);  // ошибка

MyInt myInt;
takeInt(myInt);    // ошибка
takeMyInt(myInt);  // хорошо

Параметры:
init Необязательное начальное значение для нового типа. Например:
alias MyInt = Typedef!(int, 10);
MyInt myInt;
assert(myInt == 10);  // по-умолчанию инициализировано в 10
cookie Необязательно, используется для создания нескольких уникальных типов, основанных на одном и том же исходном типе T. Например:
alias TypeInt1 = Typedef!int;
alias TypeInt2 = Typedef!int;

// Два созданных Typedef являются одним и тем же типом.
static assert(is(TypeInt1 == TypeInt2));

alias MoneyEuros = Typedef!(float, float.init, "euros");
alias MoneyDollars = Typedef!(float, float.init, "dollars");

// Два созданных Typedef _не_ являются одним и тем же типом.
static assert(!is(MoneyEuros == MoneyDollars));

Замечание: Если библиотечная процедура не может обрабатывать тип, созданный с помощью Typedef, вы можете использовать шаблон TypedefType для извлечения типа, который обёртывает Typedef.

template TypedefType(T)
Получить базовый тип, который обёртывает Typedef. Если T не создан через Typedef, то будет возвращён псевдоним T.
Примеры:
import std.typecons : Typedef, TypedefType;
import std.conv : to;

alias MyInt = Typedef!int;
static assert(is(TypedefType!MyInt == int));

/// Выполнение с типом не-Typedef вернет этот же тип
static assert(is(TypedefType!int == int));

string num = "5";

// извлечь необходимый тип
MyInt myInt = MyInt( num.to!(TypedefType!MyInt) );
assert(myInt == 5);

// приведение к базовому типу для получения завёрнутого значения 
int x = cast(TypedefType!MyInt)myInt;

alias MyIntInit = Typedef!(int, 42);
static assert(is(TypedefType!MyIntInit == int));
static assert(MyIntInit() == 42);

Переместиться к: scoped

template scoped(T) if (is(T == class))
Распределяет память под объект класса прямо внутри текущей области видимости, поэтому избегает накладных расходов new. Этот объект небезопасен; пользователь не должен выводить ссылку на объект наружу из области видимости.
Будет вызван деструктор класса при уничтожении результата самого scoped().
Scoped экземпляры классов могут быть встроены в родительский класс или структуру, точно так же, как экземпляр дочерней структуры. У переменных-членов scoped должен быть тип typeof(scoped!Class(args)), и их нужно инициализировать вызовом scoped. Ниже приведен пример.

Замечание: Недопустимо перемещать экземпляр класса, даже если вы уверены, что на него нет указателей. Таким образом, запрещено перемещать scoped объект.

Примеры:
class A
{
    int x;
    this()     {x = 0;}
    this(int i){x = i;}
    ~this()    {}
}

// Стандартное использование, построение A в стеке
auto a1 = scoped!A();
a1.x = 42;

// Результат вызова `scoped` неявно преобразуется в ссылку на класс
A aRef = a1;
assert(aRef.x == 42);

// Уничтожение scoped
{
    auto a2 = scoped!A(1);
    assert(a2.x == 1);
    aRef = a2;
    // a2 здесь уничтожается, вызывая деструктор A
}
// Теперь aRef является недействительной ссылкой

// Здесь временный объект scoped A немедленно уничтожается. 
// Это означает, что после этого ссылка на него недействительна.
version(Bug)
{
    // Неправильно, следует использовать `auto`
    A invalid = scoped!A();
}

// Ограничения
version(Bug)
{
    import std.algorithm.mutation : move;
    auto invalid = a1.move; // недопустимо, объекты scoped нельзя перемещать 
}
static assert(!is(typeof({
    auto e1 = a1; // недопустимо, объекты scoped нельзя копировать
    assert([a1][0].x == 42); // то же самое
})));
static assert(!is(typeof({
    alias ScopedObject = typeof(a1);
    auto e2 = ScopedObject();  // недопустимо, должно создаваться через scoped!A
    auto e3 = ScopedObject(1); // то же самое
})));

// Использование с помощью псевдонима 
alias makeScopedA = scoped!A;
auto a3 = makeScopedA();
auto a4 = makeScopedA(1);

// Использование в качестве переменной-члена
struct B
{
    typeof(scoped!A()) a; // обратите внимание на завершающие круглые скобки

    this(int i)
    {
        // построение члена
        a = scoped!A(i);
    }
}

// Размещение в стеке
auto b1 = B(5);
aRef = b1.a;
assert(aRef.x == 5);
destroy(b1); // вызывается деструктор A для b1.a
// Теперь aRef является недействительной ссылкой 

// Размещение в куче
auto b2 = new B(6);
assert(b2.a.x == 6);
destroy(*b2); // вызывается деструктор A для b2.a
@system auto scoped(Args...)(auto ref Args args);
Возвращает scoped-объект.
Параметры:
Args args Аргументы, передаваемые в конструктор T.

Переместиться к: Flag

template Flag(string name)
Определяет простой, самодокументируемый флаг yes/no. Он упрощает API, определяющее функции, которые принимают флаги, не прибегая к bool, который непрозрачен при вызовах, и без необходимости отдельно определять перечислимый тип. Использование Flag!"Name" вместо bool делает значение флага очевидным при вызовах. Каждый флаг yes/no имеет свой собственный тип, что делает невозможными беспорядок и путаницу.

Пример: Код, вызываюший getLine (обычно, где-то далеко от его определения) не получится понять без просмотра документации, даже для пользователей, знакомых с API:

string getLine(bool keepTerminator)
{
    ...
    if (keepTerminator) ...
    ...
}
...
auto line = getLine(false);
Если предположить обратное значение (т.е. «IgnoreTerminator») и вставить неправильный код, он скомпилируется и будет работать с ошибочными результатами.
После замены bool-параметра на экземпляр Flag, код, вызывающий getLine, могут легко прочитать и понять даже люди, не знающие API:
string getLine(Flag!"keepTerminator" keepTerminator)
{
    ...
    if (keepTerminator) ...
    ...
}
...
auto line = getLine(Yes.keepTerminator);
Структуры Yes и No предоставляются как сокращённое обозначение для Flag!"Name".yes и Flag!"Name".no и являются предпочтительными для краткости и удобочитаемости. Предпочтительно использовать эти удобные структуры вместо создания псевдонима Flag, как способа избежать ввода полного типа при указании положительного или отрицательного вариантов.
Передача категориальных данных с помощью неструктурированных bool-параметров в книге «Совершенный код» Стива Макконелла (Steve McConnell in the Code Complete) классифицируется как "simple-data coupling" вместе с тремя другими видами связи. Автор утверждает, ссылаясь на несколько исследований, что связь отрицательно влияет на качество кода. Flag предлагает простой структурированный способ для передачи флагов да/нет в API.

Переместиться к: no · yes

enum Flag: bool;
no
При создании значения типа Flag!"Name" используйте Flag!"Name".no для отрицательного варианта. При использовании значений типа Flag!"Name", сравнивайте его с Flag!"Name".no, или просто false или 0.
yes
При создании значений типа Flag!"Name" используйте Flag!"Name".yes для положительного варианта. При использовании значений типа Flag!"Name", сравнивайте его с Flag!"Name".yes.
struct Yes;

struct No;
Удобные имена, которые позволяют использовать, например, Yes.encryption вместо Flag!"encryption".yes и No.encryption вместо Flag!"encryption".no.
Примеры:
Flag!"abc" flag1;
assert(flag1 == Flag!"abc".no);
assert(flag1 == No.abc);
assert(!flag1);
if (flag1) assert(false);
flag1 = Yes.abc;
assert(flag1);
if (!flag1) assert(false);
if (flag1) {} else assert(false);
assert(flag1 == Yes.abc);
template isBitFlagEnum(E)
Определяет, является ли перечислимый тип интегральным типом из нескольких "флажковых" значений (т.е. значения с количеством бит ровно 1). Дополнительно, допускается нулевое значение для совместимости с перечислениями, включающими значение "None".
Примеры:
enum A
{
    None,
    A = 1<<0,
    B = 1<<1,
    C = 1<<2,
    D = 1<<3,
}

static assert(isBitFlagEnum!A);

enum B
{
    A,
    B,
    C,
    D // D == 3
}

static assert(!isBitFlagEnum!B);

enum C: double
{
    A = 1<<0,
    B = 1<<1
}

static assert(!isBitFlagEnum!C);
struct BitFlags(E, Flag!"unsafe" unsafe = No.unsafe) if (unsafe || isBitFlagEnum!E);
Типобезопасная структура для хранения комбинаций перечислимых значений.
Этот шаблон определяет простую структуру для представления комбинаций значений enum с побитовым ИЛИ. Его можно использовать, если все значения перечисления являются интегральными константами с числом бит не более 1, или если параметр unsafe явно установлен в Yes. Это намного безопаснее, чем использование самого перечисления enum для хранения ИЛИ-комбинации, что может привести к неожиданным эффектам:
enum E
{
    A = 1<<0,
    B = 1<<1
}
E e = E.A | E.B;
// будет выброшено исключение SwitchError
final switch (e)
{
    case E.A:
        return;
    case E.B:
        return;
}
Примеры:
BitFlags можно манипулировать с помощью обычных операторов
// Вы можете использовать такое перечисление с BitFlags сразу
enum Enum
{
    None,
    A = 1<<0,
    B = 1<<1,
    C = 1<<2
}
BitFlags!Enum flags1;
assert(!(flags1 & (Enum.A | Enum.B | Enum.C)));

// Вам нужно указать параметр `unsafe` для перечисления с заданными значениями
enum UnsafeEnum
{
    A,
    B,
    C,
    D = B|C
}
static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags2; }));
BitFlags!(UnsafeEnum, Yes.unsafe) flags3;

immutable BitFlags!Enum flags_empty;
// Построенный по умолчанию BitFlags не имеет набора значений
assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C));

// Значение можно установить с помощью оператора | 
immutable BitFlags!Enum flags_A = flags_empty | Enum.A;

// И проверять с помощью оператора &
assert(flags_A & Enum.A);

// Который является коммутируемым
assert(Enum.A & flags_A);

// BitFlags можно инициализировать с переменным количеством аргументов (variadically)
immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B);
assert((flags_AB & Enum.A) && (flags_AB & Enum.B) && !(flags_AB & Enum.C));

// Используйте оператор ~ для вычитания флагов 
immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A);
assert(!(flags_B & Enum.A) && (flags_B & Enum.B) && !(flags_B & Enum.C));

// Вы можете использовать шаблон EnumMembers для установки всех флагов
immutable BitFlags!Enum flags_all = EnumMembers!Enum;

// используйте & между BitFlags для нахождения пересечения
immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C);
assert (flags_B == (flags_BC & flags_AB));

// Для всех бинарных операторов работают их версии с присвоением 
BitFlags!Enum temp = flags_empty;
temp |= flags_AB;
assert(temp == (flags_empty | flags_AB));
temp = flags_empty;
temp |= Enum.B;
assert(temp == (flags_empty | Enum.B));
temp = flags_empty;
temp &= flags_AB;
assert(temp == (flags_empty & flags_AB));
temp = flags_empty;
temp &= Enum.A;
assert(temp == (flags_empty & Enum.A));

// BitFlags без установленных значений вычисляется в false
assert(!flags_empty);

// BitFlags с хотя бы одним установленным значением вычисляется в true
assert(flags_A);

// Это может быть полезно для проверки пересечений между BitFlags
assert(flags_A & flags_AB);
assert(flags_AB & Enum.A);

// Наконец, вы, конечно,  можете получать сырое значение флагов
auto value = cast(int)flags_A;
assert(value == Enum.A);
template ReplaceType(From, To, T...)
Заменяет все вхождения From в To в одном или нескольких типах T. Например, ReplaceType!(int, uint, Tuple!(int, float)[string]) даcт Tuple!(uint, float)[string]. Типы, в которых выполняется замена, могут быть произвольно сложными, включать квалификаторы, конструкторы встроенных типов (указатели, массивы, ассоциативные массивы, функции и делегаты) и экземпляры шаблонов; замена производится транзитивно через определение типа. Однако типы членов в структурах или классах не заменяются, потому что нет способов выразить типы, возникающие после замены.
Это расширенная манипуляция типами, необходимая, например, для замены типозаполнителя This в std.variant.Algebraic.
Возвращает:
ReplaceType псевдоним типа(ов), который получается после замены. ReplaceType aliases itself to the type(s) that result after replacement.
Примеры:
static assert(
    is(ReplaceType!(int, string, int[]) == string[]) &&
    is(ReplaceType!(int, string, int[int]) == string[string]) &&
    is(ReplaceType!(int, string, const(int)[]) == const(string)[]) &&
    is(ReplaceType!(int, string, Tuple!(int[], float))
        == Tuple!(string[], float))
);

Переместиться к: no · opAssign · opBinary · opUnary · this · unknown · yes

struct Ternary;
Троичный тип с тремя значениями истинности:
  • Ternary.yes для true (истина)
  • Ternary.no для false (ложь)
  • Ternary.unknown как неизвестное состояние
Также известен как trinary, trivalent или trilean.
Смотрите также:
Примеры:
Ternary a;
assert(a == Ternary.unknown);

assert(~Ternary.yes == Ternary.no);
assert(~Ternary.no == Ternary.yes);
assert(~Ternary.unknown == Ternary.unknown);
enum Ternary no;

enum Ternary yes;

enum Ternary unknown;
Возможные состояния Ternary

Переместиться к: 2

pure nothrow @nogc @safe this(bool b);

pure nothrow @nogc @safe void opAssign(bool b);
Создание и присваивание из типа bool, получая no из false и yes из true.
pure nothrow @nogc @safe this(const Ternary b);
Создание троичного значения из другого троичного значения
Ternary opUnary(string s)()
if (s == "~");

Ternary opBinary(string s)(Ternary rhs)
if (s == "|");

Ternary opBinary(string s)(Ternary rhs)
if (s == "&");

Ternary opBinary(string s)(Ternary rhs)
if (s == "^");
Таблица истинности для логических операций
a b ˜a a | b a & b a ^ b
no no yes no no no
no yes yes no yes
no unknown unknown no unknown
yes no no yes no yes
yes yes yes yes no
yes unknown yes unknown unknown
unknown no unknown unknown no unknown
unknown yes yes unknown unknown
unknown unknown unknown unknown unknown