2016-08-05 6 views
2

C++のように構造体コピーコンストラクタを明示的に呼び出すことはできますか?D構造体コピーコンストラクタ

struct foo { 
    void bar() {} 
} 

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

または私は常にいくつかのvarialbeに新しい値を代入する必要があります。私はこのような何かを書くことができますか?

答えて

6

技術的には、Dにはコピーコンストラクタもありません。むしろ、構造体は後置のコンストラクタを持つことができます。例えば

struct S 
{ 
    this(this) 
    { 
    } 
} 

通常、Dは構造体をできるだけ移動してコピーしようとします。そして、それらをコピーすると、構造体のビット単位のコピーが実行され、ビット単位のコピーを超えて処理が必要な事実の後に構造体を変更するためのポストブライトコンストラクタ(存在する場合)が実行されます。あなたがメンバー

struct S 
{ 
    this(this) 
    { 
     if(i !is null) 
      i = new int(*i); 
    } 

    int* i; 
} 

コピーコンストラクタ(C++で)のディープコピーをしたい場合は、/一方で、新しい構造体/クラスを構築し、構造体に対応するメンバのコピーと各メンバーを初期化コピーされるクラス、またはコピーコンストラクタのイニシャライザリストで初期化されたもので置き換えられます。それは、コピーしてからDのポストブラットコンストラクタで起こるように突然変異しません。したがって、コピーコンストラクタと後置コンストラクタは微妙に異なります。

C++のすべてのstructs/classにコピーコンストラクタがありますが(宣言していない場合はコンパイラが常に生成します)、Dのすべての構造体にポストブラットコンストラクタがあるわけではありません。実際、ほとんどの人はそうではありません。コンパイラは、構造体にポストブラストコンストラクタを持つ別の構造体が含まれている場合は構造体を生成します。それ以外の場合は構造体を生成せず、コピーはビット単位のコピーを行います。そして、後置構築がなければ、それを暗黙または明示的に呼び出すことはできません。そのメンバ変数や関数である - 私たちは、この

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); 

コンパイルするときに、それだけでなく、その2つのメンバ変数がリストされてい

C: tuple("__postblit", "i", "s", "__xpostblit", "opAssign") 

を出力しますが、それはまた、(明示的に宣言postblitコンストラクタがある)だけでなく__xpostblitopAssignとして__postblitを持っています。 __xpostblitは、コンパイラによって生成されたポストブラットコンストラクタです(それ以上は2番目にあります)。opAssignは、コンパイラが生成した代入演算子です(Cにはポストブラットコンストラクタがあるため必要です)。それは__xpostblitなく__postblitを持っていることを

struct D 
{ 
    C[5] sa; 
} 
pragma(msg, "D: " ~ __traits(allMembers, D).stringof); 

プリント

D: tuple("sa", "__xpostblit", "opAssign") 

注意。これは、明示的に宣言された後置のコンストラクタを持っていないためです。 __xpostblitが生成され、各メンバー変数のポストブリークコンストラクターが呼び出されました。 saは、Cの静的配列であり、Cは、後置のコンストラクタを持っています。したがって、saを正しくコピーするためには、の各要素に対して、Cのポストブリークコンストラクターを呼び出す必要があります。 D__xpostblitです。 Cには__xpostblitも含まれていますが、後打ちのコンストラクタを持つメンバーはありません。したがって、__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は明示的な後書きコンストラクタであり、__xpostblitはコンパイラによって生成されたものですが、この場合structは実際には後置コンストラクタを持つメンバ変数を持っていますので、__postblitを呼び出すよりも__xpostblitが多くなります。あなたはそれがとても

__posblit: 
C's postblit 
__xposblit: 
C's postblit 

印刷し

void main() 
{ 
    import std.stdio; 
    C c; 
    writeln("__posblit:"); 
    c.__postblit(); 
    writeln("__xposblit:"); 
    c.__xpostblit(); 
} 

ていた場合は、

void main() 
{ 
    import std.stdio; 
    D d; 
    writeln("__xposblit:"); 
    d.__xpostblit(); 
} 

を持っていた場合、それは

を印刷し、一方、

は、2つの間には実質的な違いがありません

__xposblit: 
C's postblit 
C's postblit 
C's postblit 
C's postblit 
C's postblit 

Dのメンバーであるsaの各要素に対して、C 'postblitが5回呼び出されることに注意してください。また、__postblitDに呼び出すことはできませんでした。なぜなら明示的な後置生成子 - だけが暗黙的なものではないからです。

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を呼び出すと明示的に宣言されたポストブリークコンストラクターが呼び出されますが、__xpostblitはメンバー変数のポストブリーコンストラクターを呼び出します。

そしてABがposblitコンストラクタとそれらを持っていないメンバーを持っていない、ので、もちろん、彼らに__postblitまたは__xpostblitのいずれかを呼び出すことは違法になります。

したがって、ポストブリーチコンストラクタを明示的に呼び出すことはできますが、それがある場合にのみ、それを呼び出すべきではありません。関数が__で始まる場合、またはオーバーロードされた演算子の1つで、したがってopで始まる場合、明示的に呼び出される必要はほとんどありません。また、それには後置のコンストラクタも含まれます。しかし、正当な理由がある場合は、__postblitではなく__xpostblitを呼び出すことを忘れないでください。そうしないと、メンバー変数のポストブリーフは実行されません。 __traits(hasMember, S1, "__xpostblit")を実行するか、std.traitsから悪い名前のhasElaborateCopyConstructorを使用してテストすることができます(ほとんどのコードはより慣用的なのでhasElaborateCopyConstructorを使用する必要があります)。なんらかの理由で__postblitに電話したい場合は、__postblitが宣言されているかどうか気にするものがほとんどないので、std.traitsではなく__traitsでテストする必要があります。 posblitコンストラクタを気にするものは、__postblitが宣言されているかどうかに関係なく、__xpostblitを気にします。

0

Dは、それ自体がコピーコンストラクタを持っていませんが、f.tupleofが与える

foo(f.tupleof).bar() 

で(少なくともシャローコピーを作成します)既存の内容に暗黙のコンストラクタを呼び出すことができます関数の引数リストへの自動展開に適した形式の構造体メンバのリスト。