是否有可能顯式調用結構體拷貝構造函數,就像在C++中一樣?我可以寫這樣的東西:D結構體拷貝構造函數
struct foo {
void bar() {}
}
foo f;
foo(f).bar();
或者我總是需要給某些varialbe分配新的值?
是否有可能顯式調用結構體拷貝構造函數,就像在C++中一樣?我可以寫這樣的東西:D結構體拷貝構造函數
struct foo {
void bar() {}
}
foo f;
foo(f).bar();
或者我總是需要給某些varialbe分配新的值?
那麼,從技術上講,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構造)以及__xpostblit
和opAssign
。 __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構造函數。 sa
是C
的靜態數組,並且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構造函數。
,當然還有,因爲A
和B
沒有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
都可以存在。
d沒有拷貝構造函數本身,而是你可以用
foo(f.tupleof).bar()
致電與現有的一個(這將至少建立一個淺拷貝)的內容的隱式構造的f.tupleof
給適用於自動擴展到函數參數列表的表格中的結構成員列表。