是否有可能显式调用结构体拷贝构造函数,就像在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
给适用于自动扩展到函数参数列表的表格中的结构成员列表。