2016-08-05 5 views
2

Можно ли явно вызвать конструктор struct copy, как в C++? Могу ли я написать что-то вроде этого:D struct copy constructor

struct foo { 
    void bar() {} 
} 

foo f; 
foo(f).bar(); 

Или мне всегда нужно назначать новое значение для некоторого varialbe?

ответ

6

Ну, технически, 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.

0

D не имеет конструкторов копирования самих по себе, но вы могли бы назвать неявный конструктор с содержимым существующего (что бы создать неполную копию, по крайней мере) с

foo(f.tupleof).bar() 

f.tupleof дает список элементов структуры в форме, подходящей для автоматического расширения в список аргументов функции.