是否可以显式调用struct copy构造函数,就像在C ++中一样?我能写这样的东西:
{{1}}
或者我总是需要为某些varialbe分配新值?
答案 0 :(得分:9)
嗯,从技术上讲,D甚至没有复制构造函数。相反,结构可以有postblit构造函数。 e.g。
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构造函数。事实上,大多数人都没有。如果struct包含另一个具有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
,必须在C
中的每个元素上调用sa
的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
是编译器生成的构造函数,但是,在这种情况下,struct实际上有成员变量和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
调用它和成员变量的postblit构造函数。
当然,由于A
和B
没有posblit构造函数,也没有拥有它们的成员,因此调用__postblit
或{{{ 1}}在他们身上。
所以,是的,你可以明确地调用postblit构造函数 - 但只有它有一个,你几乎肯定不应该调用它。如果一个函数以__xpostblit
开头,或者它是一个重载的运算符(因此以__
开头),那么它几乎从不应该被显式调用 - 并且包括postblit构造函数。但如果您确实找到了合理的理由来调用它,请记住您可能想要调用op
而不是__xpostblit
,否则会员变量的postblit会赢得&#t; t跑。您可以通过执行__postblit
或使用std.traits中名称较差的hasElaborateCopyConstructor
来测试它(大多数代码都应使用__traits(hasMember, S1, "__xpostblit")
,因为它更具惯用性)。如果您出于某种原因想要调用hasElaborateCopyConstructor
,那么您需要使用__postblit
而不是std.traits来测试它,因为druntime之外的任何事情都不关心是否声明了类型{ {1}}。那些关心posblit构造函数的东西关心__traits
,因为无论是否声明__postblit
都可以存在。
答案 1 :(得分:1)
D本身没有复制构造函数,但您可以使用现有的内容(至少可以创建浅层副本)调用隐式构造函数
foo(f.tupleof).bar()
f.tupleof
以适合自动扩展到函数参数列表的形式给出结构成员列表。