参照恐い
D において変数を同じ型の別の変数へ代入したとき、
・基本型、構造体は値渡し
・class 型は参照渡し
でコピーが行われる。
このことに曖昧さはない。
曖昧なのは配列のコピーである。
以下コード。
import house.tostring; struct S { private char[] value; static S opCall(char[] value) { S s; s.value = value; return s; } char[] toString() { return "{" ~ value ~ "}"; } } import std.stdio; void main() { char[] str = "ABC"; S s1 = S(str); S s2 = s1; str.writefln(); ToString(s1).writefln(); ToString(s2).writefln(); str[1] = 'b'; str.writefln(); ToString(s1).writefln(); ToString(s2).writefln(); } ... ABC {ABC} {ABC} AbC {AbC} {AbC}
あ〜あ、しっかり共有されちゃってる。
値のコピーが当たり前な C++ になじんだ者してはかなり異質に感じる。
静的配列はどうであろうか?
struct S { private char[3] value; static S opCall(char[] value) { S s; s.value[] = value; return s; } // 以下同文 ... ABC {ABC} {ABC} AbC {ABC} {ABC}
おぉ、これぞ求めていたものだ。
静的配列の場合、宣言時に領域が確保されるので、代入によって参照が共有されることはないのね。
動的配列で参照が共有されないようにするには、dup を使わにゃならんなぁ。
めんどくせぇなぁ。
struct S { private char[] value; static S opCall(char[] value) { S s; s.value = value.dup; return s; } S dup() { return S(value); } char[] toString() { return "{" ~ value ~ "}"; } } import std.stdio; void main() { char[] str = "ABC"; S s1 = S(str); S s2 = s1.dup; str.writefln(); ToString(s1).writefln(); ToString(s2).writefln(); str[1] = 'b'; s1.value[2] = 'c'; str.writefln(); ToString(s1).writefln(); ToString(s2).writefln(); } ... ABC {ABC} {ABC} AbC {ABc} {ABC}
OK,OK
でも、使ってもらう人にいちいち
S s2 = s1.dup;
と書かせるのは心苦しいなぁ。
dup が必要なのは「struct S」の実装の都合なんだよなぁ。
まぁ、copy on write に慣れた方なら「そーいうもんなんだ」と納得してくれるかもだが、やはり心苦しい。
動的配列ってポインターみたいなもんと割り切るしかないですねぇ。
Java から D に来る人も C++ から来る人もとまどいそうだ。
ところで配列と言えば連想配列もあるが、連想配列は動的なもんだから値によるコピーはまずダメでしょうな。
import tostring; struct S { char[][char[]] map; static S opCall(char[][char[]] map) { S s; s.map = map; return s; } char[] toString() { return "{" ~ ToString(map) ~ "}"; } } import std.string; void main() { char[][char[]] map; map["ABC"] = "abc"; map["DEF"] = "def"; map["GHI"] = "ghi"; S s1 = S(map); S s2 = s1; ToString(map).writefln(); ToString(s1).writefln(); ToString(s2).writefln(); map.keys[0][1] = 'Z'; map.values[1][1] = 'z'; ToString(map).writefln(); ToString(s1).writefln(); ToString(s2).writefln(); writefln("ABC" in s1.map); writefln("AZC" in s1.map); writefln(s1.map["ABC"]); writefln(s1.map["AZC"]); } ... [(ABC:abc),(DEF:def),(GHI:ghi)] {[(ABC:abc),(DEF:def),(GHI:ghi)]} {[(ABC:abc),(DEF:def),(GHI:ghi)]} [(AZC:abc),(DEF:dzf),(GHI:ghi)] {[(AZC:abc),(DEF:dzf),(GHI:ghi)]} {[(AZC:abc),(DEF:dzf),(GHI:ghi)]} 0 0 Error: ArrayBoundsError refmap(36)
やっぱりねぇ。
しかもしっかり key が壊されてて順次アクセスしかできなくなってるし(笑)。