D struct copy构造函数

时间:2016-08-05 09:17:20

标签: constructor d copy-constructor

是否可以显式调用struct copy构造函数,就像在C ++中一样?我能写这样的东西:

{{1}}

或者我总是需要为某些varialbe分配新值?

2 个答案:

答案 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构造函数)以及__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,必须在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构造函数。

当然,由于AB没有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以适合自动扩展到函数参数列表的形式给出结构成员列表。