2016-08-05 200 views
2

是否有可能顯式調用結構體拷貝構造函數,就像在C++中一樣?我可以寫這樣的東西:D結構體拷貝構造函數

struct foo { 
    void bar() {} 
} 

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

或者我總是需要給某些varialbe分配新的值?

回答

6

那麼,從技術上講,D甚至沒有複製構造函數。相反,結構可以有postblit構造函數。例如

struct S 
{ 
    this(this) 
    { 
    } 
} 

一般來說,D會嘗試儘可能多地移動結構,而不是複製它們。當它複製它們時,它會執行結構的按位副本,然後運行postblit構造函數(如果有的話)在事實之後對結構進行變異,以執行需要在按位副本之外完成的事情。如果你想要一個構件

struct S 
{ 
    this(this) 
    { 
     if(i !is null) 
      i = new int(*i); 
    } 

    int* i; 
} 

複製構造(在C++)的深層副本,在另一方面,構建了新的結構/類,並在該結構的每個構件與相應的構件的副本初始化/類正在被複制 - 或者與在複製構造函數的初始化程序列表中初始化的任何東西一樣。它不會複製,然後像D的postblit構造函數那樣發生變異。所以,一個拷貝構造函數和一個postblit構造函數是微妙地不同的。

這樣做的副作用之一是,儘管C++中的所有結構體/類都有複製構造函數(如果不聲明一個,編譯器總會爲您生成一個),但並非D中的所有結構體都有postblit構造函數。實際上,大多數不會。如果結構體包含另一個具有postblit構造函數的結構體,編譯器將生成一個,否則,它不會生成一個結構體,而複製只會執行按位副本。而且,如果沒有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構造)以及__xpostblitopAssign__xpostblit是編譯器生成的postblit構造函數(更多信息在第二個中),opAssign是編譯器生成的賦值運算符(這是必需的,因爲C具有postblit構造函數)。

struct D 
{ 
    C[5] sa; 
} 
pragma(msg, "D: " ~ __traits(allMembers, D).stringof); 

打印

D: tuple("sa", "__xpostblit", "opAssign") 

注意,它有__xpostblit但不__postblit。這是因爲它沒有explitly聲明的postblit構造函數。生成了__xpostblit以調用每個成員變量的postblit構造函數。 saC的靜態數組,並且C具有postblit構造函數。因此,要正確複製sa,必須在sa中的每個元素上調用C的postblit構造函數。 D__xpostblit這樣做。 C也有__xpostblit,但它沒有任何帶postblit構造函數的成員,所以它的__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。並且我們不能在D上調用__postblit,因爲它沒有明確的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將其稱爲成員變量的postblit構造函數。

,當然還有,因爲AB沒有posblit構造函數和沒有任何成員有他們,那將是非法的調用其中__postblit__xpostblit他們。

所以,是的,你可以明確地調用postblit構造函數 - 但只有當它有一個時,你幾乎肯定不應該調用它。如果函數以__開頭,或者它是重載操作符之一(因此以op開頭),那麼它幾乎不應該明確調用 - 並且包含postblit構造函數。但是如果你確實找到了一個合理的理由來調用它,請記住,你可能打算調用__xpostblit而不是__postblit,否則將不會運行成員變量的postblit。您可以通過執行__traits(hasMember, S1, "__xpostblit")或使用std.traits中名稱不正確的hasElaborateCopyConstructor進行測試(大多數代碼應使用hasElaborateCopyConstructor,因爲它比較習慣)。如果你想因爲某種原因致電__postblit,你需要用__traits而不是std.traits來測試它,因爲除了druntime之外幾乎沒有任何東西在意是否聲明__postblit。關心posblit構造函數的東西關心的是__xpostblit,因爲無論是否聲明瞭__postblit都可以存在。

0

d沒有拷貝構造函數本身,而是你可以用

foo(f.tupleof).bar() 

致電與現有的一個(這將至少建立一個淺拷貝)的內容的隱式構造的f.tupleof給適用於自動擴展到函數參數列表的表格中的結構成員列表。