Spec-Zone.ru › D

std.typecons

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

Категория Функции
Кортеж isTuple Tuple tuple reverse
Флаги BitFlags isBitFlagEnum Flag No Yes
Выделение памяти RefCounted refCounted RefCountedAutoInitialize scoped Unique
Генерация кода AutoImplement BlackHole generateAssertTrap generateEmptyFunction WhiteHole
Возможные значения NULL Nullable nullable NullableRef nullableRef
Прокси Proxy rebindable Rebindable ReplaceType unwrap wrap
Типы alignForSize Ternary Typedef TypedefType UnqualRef

Лицензия:
Boost License 1.0.
Исходный код
std/typecons.d
Авторы:
Andrei Alexandrescu, Bartosz Milewski, Don Clugston, Shin Fujishiro, Kenji Hara
Примеры:
// value tuples
alias Coord = Tuple!(int, "x", int, "y", int, "z");
Coord c;
c[1] = 1;       // access by index
c.z = 1;        // access by given name
writeln(c); // Coord(0, 1, 1)

// names can be omitted
alias DicEntry = Tuple!(string, string);

// tuples can also be constructed on instantiation
writeln(tuple(2, 3, 4)[1]); // 3
// construction on instantiation works with names too
writeln(tuple!("x", "y", "z")(2, 3, 4).y); // 3

// Rebindable references to const and immutable objects
{
    class Widget { void foo() const @safe {} }
    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;
}
struct Unique(T);

Капсулирует уникальную собственность ресурса.

Когда Unique!T выходит за пределы области видимости, он вызовет destroy для ресурса T, который он управляет, если он не передан. Важным следствием destroy является то, что он вызовет деструктор ресурса T. Управляемые СУБД ссылки не гарантируются как действительные во время вызова деструктора, но другие члены T, такие как дескрипторы файлов или указатели на malloc память, останутся действительными во время вызова деструктора. Это позволяет ресурсу T освободить или очистить любые ресурсы, не управляемые СУБД.

Если желательно сохранить Unique!T за пределами исходной области видимости, то он может быть передан. Передача может быть явной, вызвав release, или неявной, при возвращении Unique из функции. Ресурс T может быть объектом полиморфного класса или экземпляром интерфейса, в этом случае Unique также ведет себя полиморфно.

Если T — это тип значения, то Unique!T будет реализовано как ссылка на T.

Примеры:
static struct S
{
    int i;
    this(int i){this.i = i;}
}
Unique!S produce()
{
    // Construct a unique instance of S on the heap
    Unique!S ut = new S(5);
    // Implicit transfer of ownership
    return ut;
}
// Borrow a unique resource by ref
void increment(ref Unique!S ur)
{
    ur.i++;
}
void consume(Unique!S u2)
{
    writeln(u2.i); // 6
    // Resource automatically deleted here
}
Unique!S u1;
assert(u1.isEmpty);
u1 = produce();
increment(u1);
writeln(u1.i); // 6
//consume(u1); // Error: u1 is not copyable
// Transfer ownership of the resource
consume(u1.release);
assert(u1.isEmpty);
alias RefT = T;

Представляет ссылку на T. Преобразуется в T* если T является типом значения.

Unique!T create(A...)(auto ref A args)
Constraints: if (__traits(compiles, new T(args)));

Позволяет безопасно создать Unique. Он создает ресурс и гарантирует уникальную собственность на него (если T не публикует псевдонимы this).

Примечание
Вложенные структуры/классы создать нельзя.
Параметры:
A args Аргументы для передачи в конструктор T.
static class C {}
auto u = Unique!(C).create();
this(RefT p);

Конструктор, принимающий rvalue. Он гарантирует уникальность, пока rvalue не является просто представлением lvalue (например, преобразование). Типичное использование:

Unique!Foo f = new Foo;

this(ref RefT p);

Конструктор, принимающий lvalue. Он обнуляет свой источник. Обнуление гарантирует уникальность, пока не существует предыдущих псевдонимов к источнику.

this(U)(Unique!U u)
Constraints: if (is(u.RefT : RefT));

Конструктор, принимающий Unique типа, преобразуемого в наш тип.

Обычно используется для передачи Unique rvalue производного типа в Unique базового типа.

Пример
class C : Object {}

Unique!C uc = new C;
Unique!Object uo = uc.release;
void opAssign(U)(Unique!U u)
Constraints: if (is(u.RefT : RefT));

Передача владения от Unique типа, преобразуемого в наш тип.

const @property bool isEmpty();

Возвращает, существует ли ресурс.

Unique release();

Передача владения Unique rvalue. Обнуляет текущее содержимое. То же самое, что вызов std.algorithm.move на нём.

struct Tuple(Specs...) if (distinctFieldNames!Specs);

Кортеж значений, например, Tuple!(int, string) — это запись, которая хранит int и string. Tuple можно использовать для группировки значений, особенно при возвращении нескольких значений из функции. Если obj является Tuple, отдельные члены доступны с синтаксисом obj[0] для первого поля, obj[1] для второго и т. д.

См. также:
tuple.
Параметры:
Specs Список типов (и необязательно имён членов), которые содержит кортеж.
Примеры:
Tuple!(int, int) point;
// assign coordinates
point[0] = 5;
point[1] = 6;
// read coordinates
auto x = point[0];
auto y = point[1];
Примеры:
В Tuple члены могут быть именованными. Допускается смешивание именованных и безымянных членов. Вышеуказанный метод всё ещё применим ко всем полям.
alias Entry = Tuple!(int, "index", string, "value");
Entry e;
e.index = 4;
e.value = "Hello";
writeln(e[1]); // "Hello"
writeln(e[0]); // 4
Примеры:
Кортеж с именованными полями — это отдельный тип от кортежа с безымянными полями, то есть каждое именование даёт отдельный тип для Tuple. Два Tuple отличающиеся только именами, всё ещё являются отдельными, даже если у них может быть одинаковая структура.
Tuple!(int, "x", int, "y") point1;
Tuple!(int, int) point2;
assert(!is(typeof(point1) == typeof(point2)));
Примеры:
Использование кортежей как диапазонов
import std.algorithm.iteration : sum;
import std.range : only;
auto t = tuple(1, 2);
writeln(t.expand.only.sum); // 3
Примеры:
Конкатенация кортежей
import std.meta : AliasSeq;
auto t = tuple(1, "2") ~ tuple(ushort(42), true);
static assert(is(t.Types == AliasSeq!(int, string, ushort, bool)));
writeln(t[1]); // "2"
writeln(t[2]); // 42
writeln(t[3]); // true
alias Types = staticMap!(extractType, fieldSpecs);

Типы компонентов Tuple.

alias fieldNames = staticMap!(extractName, fieldSpecs);

Имена компонентов Tuple. У безименных полей имена пусты.

Примеры:
import std.meta : AliasSeq;
alias Fields = Tuple!(int, "id", string, float);
static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
Types expand;

Используйте t.expand для Tuple t развёртывания в компоненты. Результат expand будет таким, как будто компоненты Tuple перечислены в качестве списка значений. (Обычно Tuple действует как одно значение.)

Примеры:
auto t1 = tuple(1, " hello ", 'a');
writeln(t1.toString()); // `Tuple!(int, string, char)(1, " hello ", 'a')`

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

auto t2 = tuple(4, "test", false);
//t.expand acting as a list of values
takeSeveralTypes(t2.expand);
this(Types values);

Конструктор, принимающий по одному значению для каждого поля.

Параметры:
Types values Список значений, которые имеют тот же тип, что и в поле Types данного Tuple, или могут быть неявным образом преобразованы к этим типам. Они должны быть в том же порядке, что и в Types.
Примеры:
alias ISD = Tuple!(int, string, double);
auto tup = ISD(1, "test", 3.2);
writeln(tup.toString()); // `Tuple!(int, string, double)(1, "test", 3.2)`
this(U, size_t n)(U[n] values)
Constraints: if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types));

Конструктор, принимающий совместимый массив.

Параметры:
U[n] values Совместимый статический массив для построения Tuple. Срезы массивов не поддерживаются.
Примеры:
int[2] ints;
Tuple!(int, int) t = ints;
this(U)(U another)
Constraints: if (areBuildCompatibleTuples!(typeof(this), U));

Конструктор, принимающий совместимый Tuple. Два Tuple совместимы если они имеют одинаковую длину и для каждого типа 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);

//Ok, int can implicitly convert to double
DubVec dv = iv;
//Error: double cannot implicitly convert to int
//IntVec iv2 = dv;
bool opEquals(R)(R rhs)
Constraints: if (areCompatibleTuples!(typeof(this), R, "=="));

const bool opEquals(R)(R rhs)
Constraints: if (areCompatibleTuples!(typeof(this), R, "=="));

bool opEquals(R...)(auto ref R rhs)
Constraints: if (R.length > 1 && areCompatibleTuples!(typeof(this), Tuple!R, "=="));

Сравнение на равенство. Два Tuple считаются равными если они соответствуют следующим критериям:

  • Каждая пара Tuple имеет одинаковую длину.
  • Для каждого типа T слева и каждого типа U справа значения типа T могут быть сравнены со значениями типа U.
  • Для каждого значения v1 слева и каждого значения v2 справа выражение v1 == v2 истинно.

Параметры:
R rhs Tuple для сравнения. Он должен соответствовать критериям сравнения Tuple.
Возвращает:
true, если обе Tuple равны, иначе false.
Примеры:
Tuple!(int, string) t1 = tuple(1, "test");
Tuple!(double, string) t2 =  tuple(1.0, "test");
//Ok, int can be compared with double and
//both have a value of 1
writeln(t1); // t2
int opCmp(R)(R rhs)
Constraints: if (areCompatibleTuples!(typeof(this), R, "<"));

const int opCmp(R)(R rhs)
Constraints: if (areCompatibleTuples!(typeof(this), R, "<"));

Сравнение для упорядочивания.

Параметры:
R rhs Tuple для сравнения. Он должен соответствовать критериям сравнения 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);

//Only the first result matters for comparison
tup1[0] = 2;
assert(tup1 > tup2);
auto opBinary(string op, T)(auto ref T t)
Constraints: if (op == "~" && !(is(T : U[], U) && isTuple!U));

auto opBinaryRight(string op, T)(auto ref T t)
Constraints: if (op == "~" && !(is(T : U[], U) && isTuple!U));

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

Параметры:
T t Tuple для объединения
Возвращает:
Объединение этого кортежа и t
ref Tuple opAssign(R)(auto ref R rhs)
Constraints: if (areCompatibleTuples!(typeof(this), R, "="));

Присваивание из другого Tuple.

Параметры:
R rhs Источник Tuple для присваивания. Каждый элемент исходного Tuple должен быть неявным образом присваиваемым к соответствующему элементу целевого Tuple .
inout ref auto rename(names...)() return
Constraints: if (names.length == 0 || allSatisfy!(isSomeString, typeof(names)));

Переименовывает элементы Tuple.

rename использует переданные names и возвращает новый Tuple с этими именами, при этом содержимое не изменяется. Если передано меньше имён, чем членов Tuple, то оставшиеся члены не изменяются. Пустая строка удалит имя этого члена. Ошибка компиляции при передаче большего количества имён, чем членов Tuple.

Примеры:
auto t0 = tuple(4, "hello");

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

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

Tuple!(int, "a", int, int, "c") t2;
t2 = tuple(3,4,5);
auto t2Named = t2.rename!("", "b");
// "a" no longer has a name
static assert(!__traits(hasMember, typeof(t2Named), "a"));
writeln(t2Named[0]); // 3
writeln(t2Named.b); // 4
writeln(t2Named.c); // 5

// not allowed to specify more names than the tuple has members
static assert(!__traits(compiles, t2.rename!("a","b","c","d")));

// use it in a range 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;
writeln(res); // 68

const tup = Tuple!(int, "a", int, "b")(2, 3);
const renamed = tup.rename!("c", "d");
writeln(renamed.c + renamed.d); // 5
inout ref auto rename(alias translate)()
Constraints: if (is(typeof(translate) : V[K], V, K) && isSomeString!V && (isSomeString!K || is(K : size_t)));

Перегрузка rename, принимающая ассоциативный массив translate в качестве шаблона параметра, где ключи — либо имена, либо индексы меняемых членов, а новые имена — соответствующие значения. Каждый ключ в translate должен быть именем члена tuple. Те же правила для пустых строк применяются, что и для перегрузки rename с переменным шаблоном.

Примеры:
//replacing names by their current name

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

Tuple!(int, "a", int, "b") t2;
t2 = tuple(3, 4);
auto t2Named = t2.rename!(["a": "b", "b": "c"]);
writeln(t2Named.b); // 3
writeln(t2Named.c); // 4

const t3 = Tuple!(int, "a", int, "b")(3, 4);
const t3Named = t3.rename!(["a": "b", "b": "c"]);
writeln(t3Named.b); // 3
writeln(t3Named.c); // 4
Примеры:
//replace names by their position

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

Tuple!(int, "a", int, "b", int, "c") t2;
t2 = tuple(3, 4, 5);
auto t2Named = t2.rename!([0: "c", 2: "a"]);
writeln(t2Named.a); // 5
writeln(t2Named.b); // 4
writeln(t2Named.c); // 3
inout @property ref @trusted inout(Tuple!(sliceSpecs!(from, to))) slice(size_t from, size_t to)()
Constraints: if (from <= to && (to <= Types.length));

Возвращает срез этого Tuple по ссылке.

Параметры:
from Позиция начала среза.
to Позиция конца среза (исключительно).
Возвращает:
Новый 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);

// https://issues.dlang.org/show_bug.cgi?id=15645
Tuple!(int, short, bool, double) b;
static assert(!__traits(compiles, b.slice!(2, 4)));
const nothrow @safe size_t toHash();

Создаёт хеш этого Tuple.

Возвращает:
Хеш этого Tuple.
const string toString()();

Преобразует в строку.

Возвращает:
Строковое представление этого Tuple.
const void toString(DG)(scope DG sink);

const void toString(DG, Char)(scope DG sink, ref scope const FormatSpec!Char fmt);

Форматирует Tuple с помощью %s, %(inner%) или %(inner%|sep%).

Форматы, поддерживаемые Tuple
Формат Описание

%s

Формат, подобный Tuple!(types)(elements formatted with %s each).

%(inner%)

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

%(inner%|sep%)

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

Параметры:
DG sink Делегат, принимающий char
FormatSpec!Char fmt std.format.FormatSpec
Примеры:
import std.format : format;

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

// Default format
writeln(format("%s", tuple("a", 1))); // `Tuple!(string, int)("a", 1)`

// One Format for each individual component
writeln(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10))); // `0x1 v 1.0000 w 0xa`
writeln(format("%#x v %.4f w %#x", tuple(1, 1.0, 10).expand)); // `0x1 v 1.0000 w 0xa`

// One Format for all components
// `>abc< & >1< & >2.3< & >[4, 5]<`
writeln(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])));

// Array of Tuples
writeln(format("%(%(f(%d) = %.1f%);  %)", tupList)); // `f(1) = 1.0;  f(2) = 4.0;  f(3) = 9.0`
Примеры:
import std.exception : assertThrown;
import std.format : format, FormatException;

// Error: %( %) missing.
assertThrown!FormatException(
    format("%d, %f", tuple(1, 2.0)) == `1, 2.0`
);

// Error: %( %| %) missing.
assertThrown!FormatException(
    format("%d", tuple(1, 2)) == `1, 2`
);

// Error: %d inadequate for double
assertThrown!FormatException(
    format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0`
);
auto reverse(T)(T t)
Ограничения: если (isTuple!T);

Создает копию Tuple с полями в обратном порядке.

Параметры:
T t Копируемая Tuple
Возвращает:
Новый Tuple.
Примеры:
auto tup = tuple(1, "2");
writeln(tup.reverse); // tuple("2", 1)
template tuple(Names...)

Создает объект Tuple, инициализированный в соответствии с заданными аргументами.

Параметры:
Names Необязательный список строк, задающих имена каждого последующего поля Tuple или список типов, к которым преобразуются элементы. Для списка имён каждое имя соответствует соответствующему полю, заданному Args. Имя не обязательно должно быть указано для каждого поля, но поскольку имена должны следовать в порядке, невозможно пропустить одно поле и назвать следующее за ним. Для списка типов должно быть ровно столько же типов, сколько параметров.
Примеры:
auto value = tuple(5, 6.7, "hello");
writeln(value[0]); // 5
writeln(value[1]); // 6.7
writeln(value[2]); // "hello"

// Field names can be provided.
auto entry = tuple!("index", "value")(4, "Hello");
writeln(entry.index); // 4
writeln(entry.value); // "Hello"
auto tuple(Args...)(Args args);
Параметры:
Args args Значения для инициализации Tuple. Тип Tuple будет определён из типов заданных значений.
Возвращает:
Новый Tuple с типом, определённым из заданных аргументов.
enum auto isTuple(T);

Возвращает true тогда и только тогда, когда T является экземпляром std.typecons.Tuple.

Параметры:
T Тип для проверки.
Возвращает:
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() @safe const { return x; } }
const a = new Widget;
// Fine
a.y();
// error! can't modify const a
// a.x = 5;
// error! can't modify const a
// a = new Widget;
Примеры:
Однако, Rebindable!(Widget) позволяет переназначить, в остальном ведя себя точно как const Widget.
class Widget { int x; int y() const @safe { return x; } }
auto a = Rebindable!(const Widget)(new Widget);
// Fine
a.y();
// error! can't modify const a
// a.x = 5;
// Fine
a = new Widget;
Rebindable!T rebindable(T)(T obj)
Ограничения: если (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T);

Функция-удобство для создания Rebindable с автоматическим определением типа.

Параметры:
T obj Ссылка на объект, интерфейс, ассоциативный массив или срез массива для инициализации Rebindable
Возвращает:
Новый Rebindable, инициализированный заданной ссылкой.
Примеры:
class C
{
    int payload;
    this(int p) { payload = p; }
}
const c = new C(1);

auto c2 = c.rebindable;
writeln(c2.payload); // 1
// passing Rebindable to rebindable
c2 = c2.rebindable;

c2 = new C(2);
writeln(c2.payload); // 2

const c3 = c2.get;
writeln(c3.payload); // 2
Rebindable!T rebindable(T)(Rebindable!T obj);

Эта функция просто возвращает переданный объект Rebindable. Она полезна в случаях с общей программированием, когда заданный объект может быть как обычной class, так и Rebindable.

Параметры:
Rebindable!T obj Экземпляр Rebindable!T.
Возвращает:
obj без изменений.
Примеры:
class C
{
    int payload;
    this(int p) { payload = p; }
}
const c = new C(1);

auto c2 = c.rebindable;
writeln(c2.payload); // 1
// passing Rebindable to rebindable
c2 = c2.rebindable;
writeln(c2.payload); // 1
template UnqualRef(T) if (is(T == class) || is(T == interface))

Аналогично Rebindable!(T), но удаляет все квалификаторы из ссылки, а не только константность/неизменяемость. Основной случай использования — с общими ресурсами (иметь локальную ссылку на общие данные класса).

Параметры:
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-битовых чисел с плавающей точкой, ни для структур, объявленных как align(1).

Параметры:
E Список типов для выравнивания, представляющих поля агрегата, такого как struct или class
char[][] names Имена полей, которые необходимо выровнять.
Возвращает:
Строка, которая будет добавлена в агрегат, такой как struct или class.
Примеры:
struct Banner {
    mixin(alignForSize!(byte[6], double)(["name", "height"]));
}
struct Nullable(T);

auto nullable(T)(T t);

Определяет значение, связанное с уникальным состоянием «null», которое обозначает отсутствие значения. Если объект Nullable!T создан по умолчанию, он находится в состоянии null. Присвоение значения делает его не-null. Вызов nullify может снова установить его в состояние null.

Практически 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)
{
    //Process Mr. Doe's customer record
    auto address = queryResult.get.address;
    auto customerNum = queryResult.get.customerNum;

    //Do some things with this customer's info
}
else
{
    //Add the customer to the database
}
Примеры:
import std.exception : assertThrown;

auto a = 42.nullable;
assert(!a.isNull);
writeln(a.get); // 42

a.nullify();
assert(a.isNull);
assertThrown!Throwable(a.get);
inout this(inout T value);

Конструктор, инициализирующий this значением value.

Параметры:
T value Значение, используемое для инициализации Nullable.
const bool opEquals()(auto ref const(typeof(this)) rhs);

const bool opEquals(U)(auto ref const(U) rhs)
Constraints: if (!is(U : typeof(this)) && is(typeof(this.get == rhs)));

Если оба значения null, они равны. Если одно значение null, а другое нет, они не равны. Если оба значения не null, они равны, если их значения равны.

Примеры:
Nullable!int empty;
Nullable!int a = 42;
Nullable!int b = 42;
Nullable!int c = 27;

writeln(empty); // empty
writeln(empty); // Nullable!int.init
assert(empty != a);
assert(empty != b);
assert(empty != c);

writeln(a); // b
assert(a != c);

assert(empty != 42);
writeln(a); // 42
assert(c != 42);
string toString();

const string toString();

void toString(W)(ref W writer, ref scope const FormatSpec!char fmt)
Constraints: if (isOutputRange!(W, char));

const void toString(W)(ref W writer, ref scope const FormatSpec!char fmt)
Constraints: if (isOutputRange!(W, char));

Возвращает строку "Nullable.null", если isNull имеет значение true. В противном случае результат эквивалентен вызову std.format.formattedWrite для внутреннего значения.

Параметры:
W writer Объект, принимающий диапазон вывода
FormatSpec!char fmt std.format.FormatSpec, используемый для представления значения, если this Nullable не null
Возвращает:
Строку string, если writer и fmt не установлены; void в противном случае.
const pure nothrow @property @safe bool isNull();

Проверяет, находится ли this в состоянии null.

Возвращает:
true, если this находится в состоянии null, иначе false.
Примеры:
Nullable!int ni;
assert(ni.isNull);

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

Принудительно устанавливает this в состояние null.

Примеры:
Nullable!int ni = 0;
assert(!ni.isNull);

ni.nullify();
assert(ni.isNull);
void opAssign()(T value);

Присваивает value внутреннему состоянию. Если присвоение успешно, this становится не-null.

Параметры:
T value Значение типа T для присвоения Nullable.
void opAssign()(Nullable!T value);

Если value null, устанавливает this в null, в противном случае присваивает value.get внутреннему состоянию. Если присвоение успешно, this становится не-null.

Параметры:
Nullable!T value Значение типа Nullable!T для присвоения Nullable.
Примеры:
Если this Nullable оборачивает тип, уже имеющий значение null (например, указатель), то присвоение null этому Nullable ничем не отличается от присвоения любого другого значения типа T, и полученный код будет выглядеть очень странно. Сильно рекомендуется избегать этого, используя версию Nullable со дополнительным параметром nullValue.
inout pure nothrow @property ref @safe inout(T) get();

inout pure nothrow @property @safe inout(T) get()(inout(T) fallback);

inout pure nothrow @property @safe auto get(U)(inout(U) fallback);

Получает значение, если оно не null. Если this находится в состоянии null, и необязательный параметр fallback был предоставлен, он будет возвращён. Без fallback, вызов get со состоянием null недопустим.

Когда тип fallback отличается от типа Nullable, get(T) возвращает общий тип.

Параметры:
inout(T) fallback Значение, возвращаемое в случае, если Nullable null.
Возвращает:
Значение, хранящееся внутри Nullable.
struct Nullable(T, T nullValue);

auto nullable(alias nullValue, T)(T t)
Constraints: if (is(typeof(nullValue) == T));

Аналогично Nullable!T, но состояние null определено как конкретное значение. Например, Nullable!(uint, uint.max) это uint , который использует значение uint.max для обозначения состояния null. Nullable!(T, nullValue) более эффективен с точки зрения памяти, чем Nullable!T, так как ему не нужно хранить дополнительное bool.

Параметры:
T Тип оборачиваемого значения, для которого Nullable предоставляет значение null.
nullValue Значение null, обозначающее состояние null этого Nullable. Должно быть типа T.
Примеры:
Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle)
{
    //Find the needle, returning -1 if not found

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

void sendLunchInvite(string name)
{
}

//It's safer than C...
auto coworkers = ["Jane", "Jim", "Marry", "Fred"];
auto pos = indexOf(coworkers, "Bob");
if (!pos.isNull)
{
    //Send Bob an invitation to lunch
    sendLunchInvite(coworkers[pos]);
}
else
{
    //Bob not found; report the error
}

//And there's no overhead
static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof);
Примеры:
import std.exception : assertThrown;

Nullable!(int, int.min) a;
assert(a.isNull);
assertThrown!Throwable(a.get);
a = 5;
assert(!a.isNull);
writeln(a); // 5
static assert(a.sizeof == int.sizeof);
Примеры:
auto a = nullable!(int.min)(8);
writeln(a); // 8
a.nullify();
assert(a.isNull);
this(T value);

Конструктор, инициализирующий this значением value.

Параметры:
T value Значение, используемое для инициализации Nullable.
const @property bool isNull();

Проверяет, находится ли this в состоянии null.

Возвращает:
true, если this находится в состоянии null, иначе false.
Примеры:
Nullable!(int, -1) ni;
//Initialized to "null" state
assert(ni.isNull);

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

Принудительно устанавливает this в состояние null.

Примеры:
Nullable!(int, -1) ni = 0;
assert(!ni.isNull);

ni = -1;
assert(ni.isNull);
void opAssign()(T value);

Присваивает value внутреннему состоянию. Если присвоение успешно, this становится не-null. Проверки на null не выполняются. Обратите внимание, что присвоение может оставить this в состоянии null.

Параметры:
T value Значение типа T для присвоения Nullable. Если оно nullvalue, внутреннее состояние этого Nullable будет установлено в null.
Примеры:
Если этот Nullable оборачивает тип, уже имеющий значение null (например, указатель), и это значение null не задано для nullValue, то присвоение значения null этому Nullable ничем не отличается от присвоения любого другого значения типа T, и полученный код будет выглядеть очень странно. Сильно рекомендуется избегать этого, используя «встроенное» значение null для nullValue.
inout @property ref inout(T) get();

Получает значение. this не должен быть в состоянии null. Эта функция также вызывается для неявного преобразования в T.

Предварительные условия
isNull должен быть false.
Возвращает:
Значение, хранящееся внутри Nullable.
Примеры:
import std.exception : assertThrown, assertNotThrown;

Nullable!(int, -1) ni;
//`get` is implicitly called. Will throw
//an error in non-release mode
assertThrown!Throwable(ni == 0);

ni = 0;
assertNotThrown!Throwable(ni == 0);
template apply(alias fun)

Распаковывает содержимое Nullable, выполняет операцию и снова упаковывает. Не делает ничего, если isNull.

При вызове на Nullable, apply распакует значение, содержащееся в Nullable, передаёт его в предоставленную функцию и помещает результат в другой Nullable (если необходимо). Если Nullable null, apply вернёт null.

Параметры:
T t Nullable
fun Функция, обрабатывающая содержимое nullable
Возвращает:
fun(t.get).nullable если !t.isNull, в противном случае Nullable.init. См. также: Монад Maybe
Примеры:
alias toFloat = i => cast(float) i;

Nullable!int sample;

// apply(null) results in a null `Nullable` of the function's return type.
Nullable!float f = sample.apply!toFloat;
assert(sample.isNull && f.isNull);

sample = 3;

// apply(non-null) calls the function and wraps the result in a `Nullable`.
f = sample.apply!toFloat;
assert(!sample.isNull && !f.isNull);
writeln(f.get); // 3.0f
Примеры:
alias greaterThree = i => (i > 3) ? i.nullable : Nullable!(typeof(i)).init;

Nullable!int sample;

// when the function already returns a `Nullable`, that `Nullable` is not wrapped.
auto result = sample.apply!greaterThree;
assert(sample.isNull && result.isNull);

// The function may decide to return a null `Nullable`.
sample = 3;
result = sample.apply!greaterThree;
assert(!sample.isNull && result.isNull);

// Or it may return a value already wrapped in a `Nullable`.
sample = 4;
result = sample.apply!greaterThree;
assert(!sample.isNull && !result.isNull);
writeln(result.get); // 4
struct NullableRef(T);

auto nullableRef(T)(T* t);

Точно так же, как Nullable!T, за исключением того, что объект ссылается на значение, находящееся в другом месте памяти. Это означает, что присваивания перезаписывают начальное присвоенное значение. Внутренне NullableRef!T хранит только указатель на T (т.е. Nullable!T.sizeof == (T*).sizeof).

Примеры:
import std.exception : assertThrown;

int x = 5, y = 7;
auto a = nullableRef(&x);
assert(!a.isNull);
writeln(a); // 5
writeln(x); // 5
a = 42;
writeln(x); // 42
assert(!a.isNull);
writeln(a); // 42
a.nullify();
writeln(x); // 42
assert(a.isNull);
assertThrown!Throwable(a.get);
assertThrown!Throwable(a = 71);
a.bind(&y);
writeln(a); // 7
y = 135;
writeln(a); // 135
pure nothrow @safe this(T* value);

Конструктор связывает this с value.

Параметры:
T* value Значение, которое нужно связать.
pure nothrow @safe void bind(T* value);

Связывает внутреннее состояние с value.

Параметры:
T* value Указатель на значение типа T для привязки этого NullableRef.
Примеры:
NullableRef!int nr = new int(42);
writeln(nr); // 42

int* n = new int(1);
nr.bind(n);
writeln(nr); // 1
const pure nothrow @property @safe bool isNull();

Возвращает true тогда и только тогда, когда this находится в нулевом состоянии.

Возвращаемое значение:
true, если this находится в нулевом состоянии, в противном случае 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();

Принудительно переводит this в нулевое состояние.

Примеры:
NullableRef!int nr = new int(42);
assert(!nr.isNull);

nr.nullify();
assert(nr.isNull);
void opAssign()(T value)
Constraints: 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);
writeln(nr); // 42
inout pure nothrow @property ref @safe inout(T) get();

Получает значение. this не должно быть в нулевом состоянии. Эта функция также вызывается для неявного преобразования в T.

Примеры:
import std.exception : assertThrown, assertNotThrown;

NullableRef!int nr;
//`get` is implicitly called. Will throw
//an error in non-release mode
assertThrown!Throwable(nr == 0);

nr.bind(new int(0));
assertNotThrown!Throwable(nr == 0);
template BlackHole(Base)

BlackHole!Base является подклассом Base, который автоматически реализует все абстрактные функции-члены в Base как функции «ничего не делать». Каждая автоматически реализованная функция просто возвращает значение по умолчанию для типа возвращаемого значения, не выполняя никаких действий.

Название взято из модуля Perl Class::BlackHole от Шона М. Берка.

Параметры:
Base Нефинальный класс для наследования BlackHole.
См. также:
AutoImplement, generateEmptyFunction
Примеры:
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);
writeln(c.value); // 42

// Returns real.init which is NaN
assert(c.realValue.isNaN);
// Abstract functions are implemented as do-nothing
c.doSomething();
template WhiteHole(Base)

WhiteHole!Base является подклассом Base, который автоматически реализует все абстрактные функции-члены как функции, которые всегда завершаются ошибкой. Эти функции просто выбрасывают Error и никогда не возвращаются. Whitehole полезна для перехвата использования функций-членов класса, которые не были реализованы.

Название взято из модуля Perl Class::WhiteHole от Майкла Дж. Шверна.

Параметры:
Base Нефинальный класс для наследования WhiteHole.
См. также:
AutoImplement, generateAssertTrap
Примеры:
import std.exception : assertThrown;

static class C
{
    abstract void notYetImplemented();
}

auto c = new WhiteHole!C;
assertThrown!NotImplementedError(c.notYetImplemented()); // throws an Error
class AutoImplement(Base, alias how, alias what = isAbstractFunction) if (!is(how == class)): Base;

class AutoImplement(Interface, BaseClass, alias how, alias what = isAbstractFunction) if (is(Interface == interface) && is(BaseClass == class)): BaseClass, Interface;

AutoImplement автоматически реализует (по умолчанию) все абстрактные функции-члены в классе или интерфейсе Base указанным способом.

Второй вариант AutoImplement автоматически реализует Interface, наследовавшись от BaseClass.

Параметры:
how шаблон, который определяет, как будут реализованы/переопределены функции. Две переменные передаются в how: тип Base и псевдоним реализованной функции. Затем how должен вернуть реализованное тело функции в виде строки. Сгенерированное тело функции может использовать следующие ключевые слова:
  • a0, a1, …: аргументы, передаваемые в функцию;
  • args: кортеж аргументов;
  • self: псевдоним самой функции;
  • parent: псевдоним переопределенной функции (если она есть).
Возможно, вы захотите использовать шаблонные свойства функций (вместо неявных шаблонных свойств) для создания сложных функций:
// Prints log messages for each call to overridden 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 означает, что переданная функция должна быть реализована/переопределена.
// Sees if fun returns something.
enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void);
Примечание
Сгенерированный код вставляется в область видимости модуля std.typecons. Таким образом, любые полезные функции за пределами std.typecons не могут быть использованы в сгенерированном коде. Чтобы обойти эту проблему, вы можете import необходимые вещи в локальной структуре, как это сделано в шаблоне generateLogger() в примере выше.
Ошибки:
  • Переменные аргументы конструкторов не передаются в суперкласс.
  • Глубокое наследование интерфейса приводит к ошибке компиляции с сообщениями типа «Ошибка: функция std.typecons.AutoImplement!(Foo).AutoImplement.bar не переопределяет ни одну функцию». [Bugzilla 2525]
  • Ключевое слово parent на самом деле является делегатом на соответствующую функцию-член суперкласса. [Bugzilla 2540]
  • Использование параметра шаблона alias в how и/или what может привести к странной ошибке компиляции. Для решения этой проблемы используйте параметр шаблона кортежа вместо него. [Bugzilla 4217]
Примеры:
interface PackageSupplier
{
    int foo();
    int bar();
}

static abstract class AbstractFallbackPackageSupplier : PackageSupplier
{
    protected PackageSupplier default_, fallback;

    this(PackageSupplier default_, PackageSupplier fallback)
    {
        this.default_ = default_;
        this.fallback = fallback;
    }

    abstract int foo();
    abstract int bar();
}

template fallback(T, alias func)
{
    import std.format : format;
    // for all implemented methods:
    // - try default first
    // - only on a failure run & return fallback
    enum fallback = q{
        scope (failure) return fallback.%1&dollar;s(args);
        return default_.%1&dollar;s(args);
    }.format(__traits(identifier, func));
}

// combines two classes and use the second one as fallback
alias FallbackPackageSupplier = AutoImplement!(AbstractFallbackPackageSupplier, fallback);

class FailingPackageSupplier : PackageSupplier
{
    int foo(){ throw new Exception("failure"); }
    int bar(){ return 2;}
}

class BackupPackageSupplier : PackageSupplier
{
    int foo(){ return -1; }
    int bar(){ return -1;}
}

auto registry = new FallbackPackageSupplier(new FailingPackageSupplier(), new BackupPackageSupplier());

writeln(registry.foo()); // -1
writeln(registry.bar()); // 2
template generateEmptyFunction(C, func...)

enum string generateAssertTrap(C, func...);

Предопределенные политики how для AutoImplement. Эти шаблоны также используются BlackHole и WhiteHole соответственно.

Примеры:
alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction);

interface I
{
    int foo();
    string bar();
}

auto i = new BlackHole!I();
// generateEmptyFunction returns the default value of the return type without doing anything
writeln(i.foo); // 0
assert(i.bar is null);
Примеры:
import std.exception : assertThrown;

alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap);

interface I
{
    int foo();
    string bar();
}

auto i = new WhiteHole!I();
// generateAssertTrap throws an exception for every unimplemented function of the interface
assertThrown!NotImplementedError(i.foo);
assertThrown!NotImplementedError(i.bar);
template wrap(Targets...) if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))

template wrap(Targets...) if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))

template unwrap(Target) if (isMutable!Target)

template unwrap(Target) if (!isMutable!Target)

Поддержка структурно-ориентированных безопасных преобразований.

Если Source имеет структурное соответствие с interface Targets, wrap создаёт внутренний класс-обёртку, который наследуется от Targets и обёртки src объект, затем возвращает его.

unwrap может быть использован для извлечения объектов, которые были обернуты с помощью 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();
}

// does not have structural conformance
static assert(!__traits(compiles, d1.wrap!Refleshable));
static assert(!__traits(compiles, h1.wrap!Refleshable));

// strict upcast
Quack qd = d1.wrap!Quack;
assert(qd is d1);
assert(qd.quack() == 1);    // calls Duck.quack
// strict downcast
Duck d2 = qd.unwrap!Duck;
assert(d2 is d1);

// structural upcast
Quack qh = h1.wrap!Quack;
assert(qh.quack() == 2);    // calls Human.quack
// structural downcast
Human h2 = qh.unwrap!Human;
assert(h2 is h1);

// structural upcast (two steps)
Quack qx = h1.wrap!Quack;   // Human -> Quack
Flyer fx = qx.wrap!Flyer;   // Quack -> Flyer
assert(fx.height == 20);    // calls Human.height
// structural downcast (two steps)
Quack qy = fx.unwrap!Quack; // Flyer -> Quack
Human hy = qy.unwrap!Human; // Quack -> Human
assert(hy is h1);
// structural downcast (one step)
Human hz = fx.unwrap!Human; // Flyer -> Human
assert(hz is h1);
Примеры:
import std.traits : FunctionAttribute, functionAttributes;
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;
writeln(a.run()); // 1
writeln(b.stop()); // 2
writeln(b.status); // 3
static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
enum RefCountedAutoInitialize: int;

Варианты автоматической инициализации объекта RefCounted (см. определение RefCounted ниже).

Примеры:
import core.exception : AssertError;
import std.exception : assertThrown;

struct Foo
{
    int a = 42;
}

RefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto;
RefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto;

writeln(rcAuto.refCountedPayload.a); // 42

assertThrown!AssertError(rcNoAuto.refCountedPayload);
rcNoAuto.refCountedStore.ensureInitialized;
writeln(rcNoAuto.refCountedPayload.a); // 42
no

Не выполнять автоматическую инициализацию объекта

yes

Выполнить автоматическую инициализацию объекта

struct RefCounted(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes) if (!is(T == class) && !is(T == interface));

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

Экземпляр RefCounted представляет собой ссылку на структуру, которая в данной документации называется хранилищем или структурой реализации хранилища. Хранилище содержит счётчик ссылок и полезную нагрузку T. RefCounted использует malloc для выделения хранилища. По мере копирования экземпляров RefCounted или выхода их из области видимости, счётчик ссылок автоматически увеличивается или уменьшается. Когда счётчик ссылок становится равным нулю, RefCounted вызовет destroy для полезной нагрузки и вызовет free для освобождения хранилища. Если полезная нагрузка T содержит ссылки на память, выделенную под управлением сборщика мусора, RefCounted добавит её в память сборщика мусора, которая сканируется на указатели, и удалит её из сканирования сборщиком мусора до вызова free для хранилища.

Важным следствием работы destroy является вызов деструктора полезной нагрузки T. Ссылки, управляемые сборщиком мусора, не гарантированно останутся валидными во время вызова деструктора, но другие члены T, такие как дескрипторы файлов или указатели на память malloc, останутся валидными во время вызова деструктора. Это позволяет T освободить или очистить любые ресурсы, не управляемые сборщиком мусора, сразу после того, как счётчик ссылок достигнет нуля.

RefCounted небезопасен и следует использовать с осторожностью. Ссылки на полезную нагрузку не должны выходить за пределы объекта RefCounted.

Опция autoInit обеспечивает автоматическую инициализацию хранилища. Оставление autoInit == RefCountedAutoInitialize.yes (по умолчанию) удобно, но влечёт за собой проверку при каждом доступе к полезной нагрузке. Если autoInit == RefCountedAutoInitialize.no, код пользователя должен вызвать либо refCountedStore.isInitialized, либо refCountedStore.ensureInitialized перед попыткой доступа к полезной нагрузке. Пропуск этой операции приведёт к ошибке обращения к нулевому указателю.

Примеры:
// A pair of an `int` and a `size_t` - the latter being the
// reference count - will be dynamically allocated
auto rc1 = RefCounted!int(5);
writeln(rc1); // 5
// No more allocation, add just one extra reference count
auto rc2 = rc1;
// Reference semantics
rc2 = 42;
writeln(rc1); // 42
// the pair will be freed when rc1 and rc2 go out of scope
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)
Constraints: if (A.length > 0);

this(T val);

Конструктор, инициализирующий полезную нагрузку.

Постусловие
refCountedStore.isInitialized
void opAssign(typeof(this) rhs);

void opAssign(T rhs);

Операторы присваивания

@property ref @trusted T refCountedPayload() return;

inout pure nothrow @nogc @property ref @safe inout(T) refCountedPayload() return;

Возвращает ссылку на полезную нагрузку. Если (autoInit == RefCountedAutoInitialize.yes), вызывает refCountedStore.ensureInitialized. В противном случае просто выполняет assert(refCountedStore.isInitialized). Используется с alias refCountedPayload this;, чтобы вызывающие функции могли использовать объект RefCounted как T.

Первый перегрузка существует только если autoInit == RefCountedAutoInitialize.yes. Таким образом, если autoInit == RefCountedAutoInitialize.no или вызывается для константного или неизменяемого объекта, то refCountedPayload также будет квалифицироваться как безопасный и без исключений (но всё равно будет проверять на инициализацию).

RefCounted!(T, RefCountedAutoInitialize.no) refCounted(T)(T val);

Инициализирует RefCounted значением val. Шаблонный параметр T для RefCounted выводится из val. Эта функция может использоваться для перемещения некопируемых значений в кучу. Она также отключает опцию autoInit для RefCounted.

Параметры:
T val Значение, подлежащее счёту ссылок
Возвращает:
Инициализированный RefCounted, содержащий val.
См. также:
C++'s make_shared
Примеры:
static struct File
{
    string name;
    @disable this(this); // not copyable
    ~this() { name = null; }
}

auto file = File("name");
writeln(file.name); // "name"
// file cannot be copied and has unique ownership
static assert(!__traits(compiles, {auto file2 = file;}));

// make the file refcounted to share ownership
import std.algorithm.mutation : move;
auto rcFile = refCounted(move(file));
writeln(rcFile.name); // "name"
writeln(file.name); // null
auto rcFile2 = rcFile;
writeln(rcFile.refCountedStore.refCount); // 2
// file gets properly closed when last reference is dropped
template Proxy(alias a)

Создаёт прокси для значения a, которое будет перенаправлять все операции, отключая неявные преобразования. Алиасируемый элемент a должен быть lvalue. Это полезно для создания нового типа на основе "базового" типа (хотя это не отношение подтип-супертип; новый тип никак не связан со старым типом по задумке).

Новый тип поддерживает все операции, которые выполняет базовый тип, включая все операторы, такие как +, --, <, [], и т.д.

Параметры:
a Значение, выступающее в роли прокси для всех операций. Оно должно быть lvalue.
Примеры:
struct MyInt
{
    private int value;
    mixin Proxy!value;

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

MyInt n = 10;

// Enable operations that original type has.
++n;
writeln(n); // 11
writeln(n * 2); // 22

void func(int n) { }

// Disable implicit conversions to original type.
//int x = n;
//func(n);
Примеры:
Проксируемое значение должно быть lvalue.
struct NewIntType
{
    //Won't work; the literal '1'
    //is an rvalue, not an lvalue
    //mixin Proxy!1;

    //Okay, n is an lvalue
    int n;
    mixin Proxy!n;

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

NewIntType nit = 0;
nit++;
writeln(nit); // 1


struct NewObjectType
{
    Object obj;
    //Ok, obj is an lvalue
    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 гарантирует, что два типа не будут считаться равными.

Параметры:
init Необязательное начальное значение для нового типа.
cookie Необязательно, используется для создания нескольких уникальных типов, основанных на одном исходном типе T
Примечание
Если библиотечная функция не поддерживает тип Typedef, вы можете использовать шаблон TypedefType для извлечения типа, который оборачивает Typedef.
Примеры:
alias MyInt = Typedef!int;
MyInt foo = 10;
foo++;
writeln(foo); // 11
Примеры:
Значения пользовательской инициализации
alias MyIntInit = Typedef!(int, 42);
static assert(is(TypedefType!MyIntInit == int));
static assert(MyIntInit() == 42);
Примеры:
Typedef создаёт новый тип
alias MyInt = Typedef!int;
static void takeInt(int) {}
static void takeMyInt(MyInt) {}

int i;
takeInt(i);    // ok
static assert(!__traits(compiles, takeMyInt(i)));

MyInt myInt;
static assert(!__traits(compiles, takeInt(myInt)));
takeMyInt(myInt);  // ok
Примеры:
Используйте необязательный аргумент cookie для создания различных типов на основе одного базового типа
alias TypeInt1 = Typedef!int;
alias TypeInt2 = Typedef!int;

// The two Typedefs are the same type.
static assert(is(TypeInt1 == TypeInt2));

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

// The two Typedefs are _not_ the same type.
static assert(!is(MoneyEuros == MoneyDollars));
string toString(this T)();

void toString(this T, W)(ref W writer, ref scope const FormatSpec!char fmt)
Constraints: if (isOutputRange!(W, char));

Преобразование обернутого значения в удобочитаемую строку

Примеры:
import std.conv : to;

int i = 123;
auto td = Typedef!int(i);
writeln(i.to!string); // td.to!string
template TypedefType(T)

Получение базового типа, который оборачивает Typedef. Если T не является Typedef, он будет алиасирован на T.

Примеры:
import std.conv : to;

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

/// Instantiating with a non-Typedef will return that type
static assert(is(TypedefType!int == int));

string num = "5";

// extract the needed type
MyInt myInt = MyInt( num.to!(TypedefType!MyInt) );
writeln(myInt); // 5

// cast to the underlying type to get the value that's being wrapped
int x = cast(TypedefType!MyInt) myInt;

alias MyIntInit = Typedef!(int, 42);
static assert(is(TypedefType!MyIntInit == int));
static assert(MyIntInit() == 42);
template scoped(T) if (is(T == class))

Выделяет объект class непосредственно внутри текущей области видимости, тем самым избегая накладных расходов new. Данная функция небезопасна; ответственность за то, чтобы ссылка на объект не покидала область видимости, лежит на пользователе.

Деструктор класса будет вызван при уничтожении результата scoped().

Экземпляры scoped-классов могут быть вложены в родительский class или struct, как и экземпляры вложенных структур. scoped-члены должны быть типа typeof(scoped!Class(args)), и инициализироваться вызовом scoped. См. пример ниже.

Примечание
Перемещение экземпляра класса запрещено, даже если вы уверены, что на него нет ссылок. Поэтому перемещение scoped-объекта запрещено.
Примеры:
class A
{
    int x;
    this()     {x = 0;}
    this(int i){x = i;}
    ~this()    {}
}

// Standard usage, constructing A on the stack
auto a1 = scoped!A();
a1.x = 42;

// Result of `scoped` call implicitly converts to a class reference
A aRef = a1;
writeln(aRef.x); // 42

// Scoped destruction
{
    auto a2 = scoped!A(1);
    writeln(a2.x); // 1
    aRef = a2;
    // a2 is destroyed here, calling A's destructor
}
// aRef is now an invalid reference

// Here the temporary scoped A is immediately destroyed.
// This means the reference is then invalid.
version (Bug)
{
    // Wrong, should use `auto`
    A invalid = scoped!A();
}

// Restrictions
version (Bug)
{
    import std.algorithm.mutation : move;
    auto invalid = a1.move; // illegal, scoped objects can't be moved
}
static assert(!is(typeof({
    auto e1 = a1; // illegal, scoped objects can't be copied
    assert([a1][0].x == 42); // ditto
})));
static assert(!is(typeof({
    alias ScopedObject = typeof(a1);
    auto e2 = ScopedObject();  // illegal, must be built via scoped!A
    auto e3 = ScopedObject(1); // ditto
})));

// Use with alias
alias makeScopedA = scoped!A;
auto a3 = makeScopedA();
auto a4 = makeScopedA(1);

// Use as member variable
struct B
{
    typeof(scoped!A()) a; // note the trailing parentheses

    this(int i)
    {
        // construct member
        a = scoped!A(i);
    }
}

// Stack-allocate
auto b1 = B(5);
aRef = b1.a;
writeln(aRef.x); // 5
destroy(b1); // calls A's destructor for b1.a
// aRef is now an invalid reference

// Heap-allocate
auto b2 = new B(6);
writeln(b2.a.x); // 6
destroy(*b2); // calls A's destructor for b2.a
@system auto scoped(Args...)(auto ref Args args);

Возвращает scoped-объект.

Параметры:
Args args Аргументы, передаваемые конструктору T
template Flag(string name)

Определяет простой, самодокументируемый флаг «да/нет». Это упрощает API-интерфейсы для определения функций, принимающих флаги, без прибегания к bool, что делает вызовы нечитаемыми, и без необходимости отдельно определять перечисление. Использование Flag!"Name" вместо bool делает смысл флага видимым в вызовах. Каждый флаг «да/нет» имеет свой собственный тип, что делает путаницу и ошибки невозможными.

Пример
Код, вызывающий getLine (обычно расположенный далеко от его определения), не может быть понят без просмотра документации, даже пользователями, знакомыми с API:
string getLine(bool keepTerminator)
{
    ...
    if (keepTerminator) ...
    ...
}
...
auto line = getLine(false);
Предполагая обратный смысл (т.е. «ignoreTerminator») и вставив неправильный код, компиляция и выполнение приведут к ошибочным результатам. После замены булевого параметра на экземпляр 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 параметров классифицируется Стивом МакКоннелом в книге «Code Complete» как «связь простых данных», наряду с тремя другими видами связи. Автор, ссылаясь на несколько исследований, утверждает, что связь оказывает негативное влияние на качество кода. Flag предлагает простой способ структурирования для передачи флагов «да/нет» в API.
Примеры:
Flag!"abc" flag;

writeln(flag); // Flag!"abc".no
writeln(flag); // No.abc
assert(!flag);
if (flag) assert(0);
Примеры:
auto flag = Yes.abc;

assert(flag);
writeln(flag); // Yes.abc
if (!flag) assert(0);
if (flag) {} else assert(0);
перечисление 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.

структура Yes;

структура No;

Удобные имена, которые позволяют использовать, например, Yes.encryption вместо Flag!"encryption".yes и No.encryption вместо Flag!"encryption".no.

Примеры:
Flag!"abc" flag;

writeln(flag); // Flag!"abc".no
writeln(flag); // No.abc
assert(!flag);
if (flag) assert(0);
Примеры:
auto flag = Yes.abc;

assert(flag);
writeln(flag); // Yes.abc
if (!flag) assert(0);
if (flag) {} else assert(0);
шаблон 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);
структура BitFlags(E, Flag!"unsafe" unsafe = No.unsafe) if (unsafe || isBitFlagEnum!E);

Типобезопасная структура для хранения комбинаций значений перечисления.

Этот шаблон определяет простую структуру для представления комбинаций значений перечисления по битовому ИЛИ. Он может использоваться, если все значения перечисления являются целочисленными константами с количеством битов не более 1, или если параметр unsafe явно установлен в Yes. Это гораздо безопаснее, чем использование самого перечисления для хранения комбинации ИЛИ, что может привести к неожиданным результатам, например:

enum E
{
    A = 1 << 0,
    B = 1 << 1
}
E e = E.A | E.B;
// will throw SwitchError
final switch (e)
{
    case E.A:
        return;
    case E.B:
        return;
}

Примеры:
Установка значений оператором | и проверка оператором &
enum Enum
{
    A = 1 << 0,
}

// A default constructed BitFlags has no value set
immutable BitFlags!Enum flags_empty;
assert(!flags_empty.A);

// Value can be set with the | operator
immutable flags_A = flags_empty | Enum.A;

// and tested using property access
assert(flags_A.A);

// or the & operator
assert(flags_A & Enum.A);
// which commutes.
assert(Enum.A & flags_A);
Примеры:
Структура BitFlags по умолчанию не имеет установленных значений
enum Enum
{
    None,
    A = 1 << 0,
    B = 1 << 1,
    C = 1 << 2
}

immutable BitFlags!Enum flags_empty;
assert(!(flags_empty & (Enum.A | Enum.B | Enum.C)));
assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C));
Примеры:
Бинарные операции: вычитание и пересечение флагов
enum Enum
{
    A = 1 << 0,
    B = 1 << 1,
    C = 1 << 2,
}
immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B);
immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C);

// Use the ~ operator for subtracting flags
immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A);
assert(!flags_B.A && flags_B.B && !flags_B.C);

// use & between BitFlags for intersection
writeln(flags_B); // (flags_BC & flags_AB)
Примеры:
Все бинарные операторы работают и в их присваивающей форме
enum Enum
{
    A = 1 << 0,
    B = 1 << 1,
}

BitFlags!Enum flags_empty, temp, flags_AB;
flags_AB = Enum.A | Enum.B;

temp |= flags_AB;
writeln(temp); // (flags_empty | flags_AB)

temp = flags_empty;
temp |= Enum.B;
writeln(temp); // (flags_empty | Enum.B)

temp = flags_empty;
temp &= flags_AB;
writeln(temp); // (flags_empty & flags_AB)

temp = flags_empty;
temp &= Enum.A;
writeln(temp); // (flags_empty & Enum.A)
Примеры:
Преобразование в bool и int
enum Enum
{
    A = 1 << 0,
    B = 1 << 1,
}

BitFlags!Enum flags;

// BitFlags with no value set evaluate to false
assert(!flags);

// BitFlags with at least one value set evaluate to true
flags |= Enum.A;
assert(flags);

// This can be useful to check intersection between BitFlags
BitFlags!Enum flags_AB = Enum.A | Enum.B;
assert(flags & flags_AB);
assert(flags & Enum.A);

// You can of course get you raw value out of flags
auto value = cast(int) flags;
writeln(value); // Enum.A
Примеры:
Необходимо указать параметр unsafe для перечислений с пользовательскими значениями
enum UnsafeEnum
{
    A = 1,
    B = 2,
    C = 4,
    BC = B|C
}
static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags; }));
BitFlags!(UnsafeEnum, Yes.unsafe) flags;

// property access tests for exact match of unsafe enums
flags.B = true;
assert(!flags.BC); // only B
flags.C = true;
assert(flags.BC); // both B and C
flags.B = false;
assert(!flags.BC); // only C

// property access sets all bits of unsafe enum group
flags = flags.init;
flags.BC = true;
assert(!flags.A && flags.B && flags.C);
flags.A = true;
flags.BC = false;
assert(flags.A && !flags.B && !flags.C);
шаблон ReplaceType(From, To, T...)

Заменяет все вхождения From на To, в одном или нескольких типах T. Например, ReplaceType!(int, uint, Tuple!(int, float)[string]) дает Tuple!(uint, float)[string]. Типы, в которых выполняется замена, могут быть произвольно сложными, включая квалификаторы, встроенные конструкторы типов (указатели, массивы, ассоциативные массивы, функции и делегаты), и экземпляры шаблонов; замена происходит транзитивно через определение типа. Однако, типы членов в struct или class не заменяются, так как нет способов выразить типы, полученные после замены.

Это продвинутая манипуляция типами, необходимая, например, для замены типа-заполнителя This в std.variant.Algebraic.

Возвращает:
ReplaceType связывает себя с результатом замены.
Примеры:
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))
);
шаблон ReplaceTypeUnless(alias pred, From, To, T...)

Подобно ReplaceType, но не выполняет замену в типах, для которых pred оценивается как true.

Примеры:
import std.traits : isArray;

static assert(
    is(ReplaceTypeUnless!(isArray, int, string, int*) == string*) &&
    is(ReplaceTypeUnless!(isArray, int, string, int[]) == int[]) &&
    is(ReplaceTypeUnless!(isArray, int, string, Tuple!(int, int[]))
        == Tuple!(string, int[]))
);
структура Ternary;

Тройственный тип с тремя значениями истинности:

  • Ternary.yes для true
  • Ternary.no для false
  • Ternary.unknown как неизвестное состояние


Также известен как троичный, трёхвалентный или трилевый.

См. также:
Трёхзначная логика в Википедии
Примеры:
Ternary a;
writeln(a); // Ternary.unknown

writeln(~Ternary.yes); // Ternary.no
writeln(~Ternary.no); // Ternary.yes
writeln(~Ternary.unknown); // Ternary.unknown
перечисление Ternary no;

перечисление Ternary yes;

перечисление Ternary unknown;

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

чистая nothrow @nogc @safe this(bool b);

чистая nothrow @nogc @safe void opAssign(bool b);

Создание и присвоение из bool, получая no для false и yes для true.

чистая 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 == "^");

Ternary opBinary(string s)(bool rhs)
Ограничения: if (s == "|" || s == "&" || 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

© 1999–2021 The D Language Foundation
Licensed under the Boost License 1.0.
https://dlang.org/phobos/std_typecons.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API