Этот модуль реализует множество конструкторов типов, т. е. шаблонов, которые позволяют создавать новые полезные типы общего назначения.
Инкапсулирует уникальное владение ресурсом. Ресурс типа T удаляется в конце области видимости, если только он не передаётся. Передача может быть явной, путем вызова release или неявной, когда Unique
возвращается из функции. Ресурс может быть полиморфным объектом класса, и в этом случае Unique
также ведет себя полиморфно.
Примеры: static struct S
{
int i;
this(int i){this.i = i;}
}
Unique!S produce()
{
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.release);
assert(u1.isEmpty);
Представляет ссылку на 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();
|
Конструктор, который принимает r-значение. Он обеспечит уникальность, если r-значение не является просто представлением о l-значении (например, cast). Типичное использование:
Unique!Foo f = new Foo;
Конструктор, который принимает 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 r-значения. Обнуляет текущее содержимое.
Кортеж значений, например 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", "", ""));
Используйте 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);
takeSeveralTypes(t2.expand);
Конструктор, принимающий по одному значению для каждого поля.
Параметры: 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);
DubVec dv = iv;
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");
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 |
Исходный кортеж. Каждый элемент исходного кортежа должен неявно приводиться к каждому соответствующему элементу целевого кортежа.
|
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");
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")));
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.
-
const string
toString
()();
Преобразование в строку.
Возвращает: Строковое представление этого кортежа Tuple.
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`
);
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));
Создает экземпляр объекта 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");
Возвращает true
, если и только если T является экземпляром std.typecons.Tuple.
Возвращает: 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();
Примеры:
Тем не менее, обёртку
Rebindable
!(Widget) позволяется переназначать, в то время как во всех остальных случаях она ведет себя точно так же, как
const Widget.
class Widget { int x; int y() const { return x; } }
auto a = Rebindable!(const Widget)(new Widget);
a.y();
a = new Widget;
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"]));
}
Определяет значение, соединённое с отличительным «нулевым» состоянием, которое обозначает отсутствие значения. При построении по умолчанию, объект Nullable
!T инициализируется состоянием null
. Присвоение делает его не-нулевым. Вызов nullify может снова занулить его.
Фактически Nullable
!T содержит T и bool.
Примеры: struct CustomerRecord
{
string name;
string address;
int customerNum;
}
Nullable!CustomerRecord getByName(string name)
{
return Nullable!CustomerRecord.init;
}
auto queryResult = getByName("Doe, John");
if (!queryResult.isNull)
{
auto address = queryResult.address;
auto customerNum = queryResult.customerNum;
}
else
{
}
inout this(inout T
value
);
Конструктор, инициализирующий this значением value
.
Параметры: T value |
Инициализирующее значение |
const pure nothrow @property @safe bool
isNull
();
Проверяет, находится ли этот объект в нулевом состоянии.
Возвращает: true
если и только если this находится в состоянии null
, в противном случае false
.
Примеры: Nullable!int ni;
assert(ni.isNull);
ni = 0;
assert(!ni.isNull);
Переводит этот объект в состояние null
.
Примеры: Nullable!int ni = 0;
assert(!ni.isNull);
ni.nullify();
assert(ni.isNull);
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);
inout pure nothrow @property ref @safe inout(T)
get
();
Возвращает значение. this не должно быть в состоянии null
. Эта функция также вызывается при неявном преобразования в T.
Возвращает:
Значение, содержащееся внутри этого
Nullable.
Примеры: import std.exception : assertThrown, assertNotThrown;
Nullable!int ni;
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)
{
return Nullable!(size_t, size_t.max).init;
}
void sendLunchInvite(string name)
{
}
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 значением value
.
Параметры: T value |
Инициализирующее значение |
const @property bool
isNull
();
Проверяет, находится ли this в состоянии null
.
Возвращает: true
если и только если this находится в состоянии null
, в противном случае false
.
Примеры: Nullable!(int, -1) ni;
assert(ni.isNull);
ni = 0;
assert(!ni.isNull);
Переводит этот объект в состояние 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;
assertThrown!Throwable(ni == 0);
ni = 0;
assertNotThrown!Throwable(ni == 0);
Тоже, что и 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;
assertThrown!Throwable(nr == 0);
nr.bind(new int(0));
assertNotThrown!Throwable(nr == 0);
-
Слово "BlackHole" переводится как "Чёрная дыра" – прим. пер.
BlackHole
!Base является подклассом Base, который автоматически реализует все абстрактные функции-члены в Base как ничего не делающие функции. Каждая автоматически реализованная функция просто возвращает значение по умолчанию возвращаемого типа, не делая ничего.
Параметры: 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);
assert(c.realValue.isNaN);
c.doSomething();
-
Слово "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 указывает, что переданная функция должна быть реализована /переопределена.
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 d2 = qd.unwrap!Duck;
assert(d2 is d1);
Quack qh = h1.wrap!Quack;
assert(qh.quack() == 2); Human h2 = qh.unwrap!Human;
assert(h2 is h1);
Quack qx = h1.wrap!Quack; Flyer fx = qx.wrap!Flyer; assert(fx.height == 20); Quack qy = fx.unwrap!Quack; Human hy = qy.unwrap!Human; assert(hy is h1);
Human hz = fx.unwrap!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 ниже).
Не выполнять автоматическую инициализацию объекта
Выполнять автоматическую инициализацию объекта
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
-указателей.
Примеры: auto rc1 = RefCounted!int(5);
assert(rc1 == 5);
auto rc2 = rc1;
rc2 = 42;
assert(rc1 == 42);
Реализация хранилища 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");
static assert(!__traits(compiles, {auto file2 = 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);
Создает прокси для значения 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) { }
Примеры:
Проксируемое значение должно быть
l-значением.
struct NewIntType
{
int n;
mixin Proxy!n;
this(int n) { this.n = n; }
}
NewIntType nit = 0;
nit++;
assert(nit == 1);
struct NewObjectType
{
Object obj;
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);
|
cookie |
Необязательно, используется для создания нескольких уникальных типов, основанных на одном и том же исходном типе T. Например:
alias TypeInt1 = Typedef!int;
alias TypeInt2 = Typedef!int;
static assert(is(TypeInt1 == TypeInt2));
alias MoneyEuros = Typedef!(float, float.init, "euros");
alias MoneyDollars = Typedef!(float, float.init, "dollars");
static assert(!is(MoneyEuros == MoneyDollars));
|
Замечание:
Если библиотечная процедура не может обрабатывать тип, созданный с помощью Typedef
, вы можете использовать шаблон TypedefType для извлечения типа, который обёртывает Typedef
.
Получить базовый тип, который обёртывает Typedef. Если T не создан через Typedef, то будет возвращён псевдоним T.
Примеры: import std.typecons : Typedef, TypedefType;
import std.conv : to;
alias MyInt = Typedef!int;
static assert(is(TypedefType!MyInt == int));
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);
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() {}
}
auto a1 = scoped!A();
a1.x = 42;
A aRef = a1;
assert(aRef.x == 42);
{
auto a2 = scoped!A(1);
assert(a2.x == 1);
aRef = a2;
}
version(Bug)
{
A invalid = scoped!A();
}
version(Bug)
{
import std.algorithm.mutation : move;
auto invalid = a1.move; }
static assert(!is(typeof({
auto e1 = a1; assert([a1][0].x == 42); })));
static assert(!is(typeof({
alias ScopedObject = typeof(a1);
auto e2 = ScopedObject(); 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);
auto b2 = new B(6);
assert(b2.a.x == 6);
destroy(*b2);
@system auto
scoped
(Args...)(auto ref Args
args
);
Возвращает scoped-объект.
Параметры: Args args |
Аргументы, передаваемые в конструктор T. |
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;
-
При создании значения типа Flag!"Name" используйте Flag!"Name".no
для отрицательного варианта. При использовании значений типа Flag!"Name", сравнивайте его с Flag!"Name".no
, или просто false
или 0.
При создании значений типа Flag!"Name" используйте Flag!"Name".yes
для положительного варианта. При использовании значений типа Flag!"Name", сравнивайте его с Flag!"Name".yes
.
Удобные имена, которые позволяют использовать, например, 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 }
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;
final switch (e)
{
case E.A:
return;
case E.B:
return;
}
Примеры: BitFlags
можно манипулировать с помощью обычных операторов
enum Enum
{
None,
A = 1<<0,
B = 1<<1,
C = 1<<2
}
BitFlags!Enum flags1;
assert(!(flags1 & (Enum.A | Enum.B | Enum.C)));
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;
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);
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));
immutable BitFlags!Enum flags_all = EnumMembers!Enum;
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));
assert(!flags_empty);
assert(flags_A);
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))
);
Троичный тип с тремя значениями истинности:
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
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 |