2016-08-05 159 views
2

是否有可能显式调用结构体拷贝构造函数,就像在C++中一样?我可以写这样的东西:D结构体拷贝构造函数

struct foo { 
    void bar() {} 
} 

foo f; 
foo(f).bar(); 

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

回答

6

那么,从技术上讲,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构造)以及__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,必须在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构造函数。

,当然还有,因为AB没有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都可以存在。

0

d没有拷贝构造函数本身,而是你可以用

foo(f.tupleof).bar() 

致电与现有的一个(这将至少建立一个浅拷贝)的内容的隐式构造的f.tupleof给适用于自动扩展到函数参数列表的表格中的结构成员列表。