是否可以像在C++中那样显式调用struct copy构造函数?我可以写这样的代码吗:
struct foo {
void bar() {}
}
foo f;
foo(f).bar();或者我总是需要给一些变量赋新的值?
发布于 2016-08-05 23:40:23
从技术上讲,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。这是因为它没有明确声明的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")因此,与C一样,E也同时拥有__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,它都可能存在。
发布于 2016-08-05 22:25:41
D本身没有复制构造函数,但您可以使用现有构造函数的内容调用隐式构造函数(这至少会创建一个浅层复制)
foo(f.tupleof).bar()f.tupleof以适合自动扩展到函数参数列表的形式提供结构成员列表。
https://stackoverflow.com/questions/38785624
复制相似问题