Ну, технически, D даже не имеет конструкторов копирования. Скорее, структуры могут иметь постплотные конструкторы. например
struct S
{
this(this)
{
}
}
В общем, D пытается переместить структуры как можно больше, а не копировать их. И когда он копирует их, он выполняет побитную копию структуры, а затем запускает конструктор postblit (если он есть), чтобы мутировать структуру после факта, чтобы сделать материал, который нужно сделать за пределами побитовой копии - например, если вы хотите глубокую копию члена
struct S
{
this(this)
{
if(i !is null)
i = new int(*i);
}
int* i;
}
Копия конструктора (в C++), с другой стороны, создает новый STRUCT/класс и инициализирует каждый элемент с копией соответствующего члена в структуры/класс копируется - или с тем, что инициализируется в списке инициализаторов конструктора копирования. Он не копирует, а затем мутирует, как это происходит с конструктором D postblit. Таким образом, конструктор копирования и конструктор postblit отличаются друг от друга.
Одним из побочных эффектов этого является то, что, хотя все структуры/классы в C++ имеют конструкторы копирования (компилятор всегда генерирует один для вас, если вы не объявляете его), не все структуры в D имеют постблит-конструкторы. На самом деле, большинство нет. Компилятор будет генерировать один, если структура содержит другую структуру с конструктором postblit, но в противном случае она не будет генерировать ее, и копирование будет просто побитовым копированием. И, если нет постплотной конструкции, вы не можете называть ее неявно или явно.
Теперь, если мы составляем этот
struct A
{
}
pragma(msg, "A: " ~ __traits(allMembers, A).stringof);
печатает
A: tuple()
A
не имеет членов - будь то переменные или функции-члены. Ничего не объявлено, и компилятор ничего не создал.
struct B
{
A a;
string s;
}
pragma(msg, "B: " ~ __traits(allMembers, B).stringof);
печатает
B: tuple("a", "s")
Она состоит из двух членов - явно объявленные переменные-члены. У этого нет никаких функций. Объявление переменных-членов не является причиной для компилятора для создания каких-либо функций.Однако, когда мы составляем
struct C
{
this(this)
{
import std.stdio;
writeln("C's postblit");
}
int i;
string s;
}
pragma(msg, "C: " ~ __traits(allMembers, C).stringof);
печатает
C: tuple("__postblit", "i", "s", "__xpostblit", "opAssign")
не только перечислены его две переменные-члены, но он также имеет __postblit
(который является явно объявлен postblit конструктор), а также __xpostblit
и opAssign
, __xpostblit
- это конструктор postblit, сгенерированный компилятором (более того, в секунду), а opAssign
- оператор присваивания, сгенерированный компилятором (что необходимо, потому что C
имеет постблицкий конструктор).
struct D
{
C[5] sa;
}
pragma(msg, "D: " ~ __traits(allMembers, D).stringof);
печатает
D: tuple("sa", "__xpostblit", "opAssign")
Обратите внимание, что он имеет __xpostblit
, но не __postblit
. Это потому, что у него нет явно объявленного postblit-конструктора. __xpostblit
был создан для вызова конструктора postblit каждой из переменных-членов. sa
является статическим массивом C
, а C
имеет постблит-конструктор. Итак, чтобы правильно скопировать sa
, конструктор postblit для C
должен быть вызван для каждого из элементов в sa
. D
__xpostblit
делает. C
также имеет __xpostblit
, но у него нет участников с postblit constructors, поэтому его __xposblit
просто называет его __postblit
.
struct E
{
this(this)
{
import std.stdio;
writeln("E's postblit");
}
C c;
}
pragma(msg, "E: " ~ __traits(allMembers, E).stringof);
печатает
E: tuple("__postblit", "c", "__xpostblit", "opAssign")
Так, E
- как C
- имеет как __postblit
и __xpostblit
. __postblit
- это явный конструктор postblit, а __xpostblit
- это тот, который сгенерирован компилятором. Однако в этом случае структура на самом деле имеет переменные-члены с конструктором postblit, поэтому __xpostblit
имеет больше общего, чем просто позвонить __postblit
.
Если у вас
void main()
{
import std.stdio;
C c;
writeln("__posblit:");
c.__postblit();
writeln("__xposblit:");
c.__xpostblit();
}
будут печататься
__posblit:
C's postblit
__xposblit:
C's postblit
Таким образом, нет никакой разницы между ними, в то время как если бы вы
void main()
{
import std.stdio;
D d;
writeln("__xposblit:");
d.__xpostblit();
}
будут печататься
__xposblit:
C's postblit
C's postblit
C's postblit
C's postblit
C's postblit
Обратите внимание: C
'postblit получает 5 раз - один раз для каждого элемента в D
, sa
. И мы не могли назвать __postblit
на D
, потому что у него нет явного конструктора postblit - просто неявный.
void main()
{
import std.stdio;
E e;
writeln("__posblit:");
e.__postblit();
writeln("__xposblit:");
e.__xpostblit();
}
напечатает
__posblit:
E's postblit
__xposblit:
C's postblit
E's postblit
И в этом случае, мы можем видеть, что __postblit
и __xpostblit
различны. Вызов __postblit
просто вызывает явно объявленный postblit-конструктор, тогда как __xpostblit
вызывает его и постблицы-конструкторы переменных-членов.
И, конечно же, так как, A
и B
не posblit конструкторов и нет пользователей, которые имеют их, это было бы незаконно назвать либо __postblit
или __xpostblit
на них.
Итак, да, вы можете вызвать конструктор postblit явно, но только если он есть, и вы почти наверняка не должны его вызывать. Если функция начинается с __
, или это один из перегруженных операторов (и, таким образом, начинается с op
), то ее почти никогда не следует вызывать явно - и это включает конструктор postblit. Но если вы найдете законную причину для его вызова, помните, что вы, вероятно, захотите позвонить __xpostblit
, а не __postblit
, иначе postblit для переменных-членов не будет запущен. Вы можете проверить его, выполнив __traits(hasMember, S1, "__xpostblit")
или используя плохо названное hasElaborateCopyConstructor
из std.traits (большинство кода должно использовать hasElaborateCopyConstructor
, так как оно более идиоматично). Если вы хотите позвонить по телефону __postblit
по какой-то причине, вам нужно будет проверить его с помощью __traits
, а не std.traits, хотя, потому что почти ничего за пределами druntime не заботит, объявлен ли тип __postblit
. Материал, который заботится о конструкторах posblit, заботится о __xpostblit
, так как это может существовать независимо от того, был ли объявлен __postblit
.