C++とその他言語における値渡し・参照渡し

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

C++とその他言語における値渡し・参照渡し

投稿記事 by MoNoQLoREATOR » 12年前

まず次の2つの記事をご覧ください。
値渡しと参照渡し (と参照の値渡し)
関数の引数の値渡しと参照渡し

C++,Java,C#,ActionScriptの値渡し・参照渡しについて確認ができたことと思います。
値渡し・参照渡しの仕様は、その言語の特徴や考え方を最も顕著に表現するものだと私は考えています。
オブジェクト指向が前提の言語は、クラスのインスタンスがデフォルトで参照渡しとなっている傾向があるようです。
たしかにそこだけ見れば理に適っていると思います。
値渡しよりも参照渡しをする機会の方が圧倒的に多いことが予想されるからです。
しかし、プリミティブ型とリファレンス型でデフォルトの動作が異なるというのは混乱を招くことに他ならないのではないでしょうか。
例えば型を変更する必要が生じたとします。
それがプリミティブ型とリファレンス型の変更であった場合、ソースコードを大幅に書き換える必要が出てくる可能性も十分あるでしょう。
同じ理由で、C++のtemplateのようなこともできません。
また、プリミティブ型を参照渡ししたい場合や、参照の参照を行いたい場合は工夫が必要です。
以上のことから、私はC++方式を支持します。
しかし、C++の「参照」では参照先を変更するということができません。
そこで、代わりにポインタを使うことになります。
ただしポインタ演算を行う必要があるため、あまり直観的ではありません。
ゆえに私の理想と完全にマッチする値渡し・参照渡しを採用している言語は今のところ見つかっていません。(C++が最も近いことは確かです)

そこで、以前にも話したと思いますが、「コネクション」という新しい概念を提案します。
"普段は参照先のインスタンスそのものとして扱い、参照先を設定/変更する時のみコネクションであることを意識して取り扱う"
そういった概念がコネクションです。
これにより厳密性を保ちながら、より直感的なソースコードを実現することができます。

CODE:

int $ intcon0;    // 宣言。参照先はnullに設定される
++intcon0;    // 参照先がnullの状態でインスタンスにアクセスすると ぬるぽ!ランタイムエラーが発生する

int value0;
int $ intcon1(value0);    // 宣言と同時に参照先を設定する
int value1;
intcon1 $= value1;    // 参照先を変更

// コネクションのコネクション(以下ダブルコネクション)
int val0(0);
int val1(1);
int $ con(val0);
//int $$ concon(con);    // 参照先には必ずコネクションを設定 ※訂正↓
int $$ concon($con);    // 参照先がシングルコネクションであることを明示する必要がある
con $= val1;
printf("%d", con);         // 1
printf("%d", concon);    // 1

// これと同じ原理で、トリプルコネクションやクアドラプルコネクション等も使用可能です。
// ただし必ず、トリプルコネクションの参照先にはダブルコネクション、クアドラプルコネクションの参照先にはトリプルコネクション…と設定する必要があります。
// つまり未設定の状態は許されていません。

// また、自分よりも小さいコネクションにキャストすることもできます。
int $$$$ quadruplecon(c);    // c等の宣言は省略
int $$$ triplecon($$quadruplecon);
int $$ doublecon($triplecon);
// このように、コネクション名の前にダラーを付けるとキャストになります。
// $でシングルコネクションへ、$$でダブルコネクションへ、$$$でトリプルコネクションへ…とキャストされます。
ポインタはメモリの位置をメインに取り扱うのに対し、コネクションはインスタンスをメインに取り扱います。
よって普段はコネクションであるということを意識する必要はありません。
これは非常に大きな利点であると私は思います。
また、ポインタはポインタ演算によってダブルポインタからトリプルポインタに切り替えたりしますが、コネクションはそもそも考え方が違います。
キャストによって、どこを参照するのかを指定します。
「何回ポインタ演算すればよいのか」などと考える必要はありません。
「ダブルコネクションにキャスト」とストレートに指定することができるのです。


ぜひみなさんの意見をお聞きしたいです。
感想だけでもありがたいのでコメントよろしくお願いします。
最後に編集したユーザー MoNoQLoREATOR on 2013年3月10日(日) 13:58 [ 編集 3 回目 ]

ISLe
記事: 2650
登録日時: 14年前

Re: C++とその他言語における値渡し・参照渡し

投稿記事 by ISLe » 12年前

コネクションの深さを直接指定するということは、関数の引数に使えないということにならないでしょうか。
ローカルに宣言するときは一階層上を参照したいときだと思いますし、相対的に参照できないとモジュール化に使えないです。
言語仕様として致命的ではないかと。

値渡しか参照渡しかは言語仕様によるものですが、モジュール化する際にはモジュール設計者の思想が反映されるので混乱を招くことはないと思います。
実引数が書き換わるかどうかはモジュールの設計段階で決まるわけですから。
うっかり書き換えてしまうミスはしばしばありますが、それは参照渡しの理解が不足しているだけです。
逆のパターンで、参照渡しのつもりが値渡しになっていたというミスはVisualBasicで括弧を付けたときくらいしかないのでは。

呼び出し側の思想は呼び出し時に解決されるべきかと思います。
コネクションはどちらの思想にも制限を加えることになるのではないでしょうかね。


わたしがいちばん分かりやすいと思うのはVisualBasicですね。
Javaのオートボクシングも面白いですが、ご存知ですか?

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

Re: C++とその他言語における値渡し・参照渡し

投稿記事 by MoNoQLoREATOR » 12年前

>>ISLeさん
返信ありがとうございます。
第一段落について。
もしかしてこういうことでしょうか。

CODE:

void funccon(int $ con){}
void funcconcon(int $$ concon){}
int $ con;
funccon(con);    // どちらの呼び出しか判別できない
指摘されて初めて気づきました。
確かにこれは問題ですね。
シングルコネクションであることを明示する仕様にすべきでした。(これに伴って「自分"より小さい"コネクションにキャスト」ではなく「自分"以下"の…」という仕様になります)

CODE:

int $ con;
int $$ concon($con);
「ローカルに宣言するときは一階層上を参照したいとき…」という文章については、これがどういった状況を指しているのか私にはわかりません。
具体的な例をお願いします。

第二段落について。
私が懸念しているのはソースコードを変更する必要が出てきたときのことです。
例えばint型メンバ変数を2つ持つクラスAを使っていたけれどint型変数1つで十分だとわかったから思い切ってAからintに変えてしまおう、といったときに「プリミティブ型は値渡し、リファレンス型は参照渡し」という仕様になっていた場合、まず代入の部分は全て書き換えなければならないでしょう。他にも書き換えなければならない部分が出てくることでしょう。しかし、C++方式なら、うまくいけば変数の宣言の型名をAからintに変えるだけで済む場合もあるでしょう。
最も、C++方式を支持する最大の理由は「プリミティブ型を参照渡ししたい場合や、参照の参照を行いたい場合は工夫が必要」という部分です。
型名を変更したら大幅な修正が必要なのはどんな仕様でもさほど変わりませんし。

Javaのオートボクシングは初めて知りました。ありがとうございます。

ISLe
記事: 2650
登録日時: 14年前

Re: C++とその他言語における値渡し・参照渡し

投稿記事 by ISLe » 12年前

MoNoQLoREATOR さんが書きました:「ローカルに宣言するときは一階層上を参照したいとき…」という文章については、これがどういった状況を指しているのか私にはわかりません。
具体的な例をお願いします。
一階層とは限りませんでした。上位層ですね。
いろいろ問題が見付かったので最後にまとめます。
MoNoQLoREATOR さんが書きました:私が懸念しているのはソースコードを変更する必要が出てきたときのことです。
例えばint型メンバ変数を2つ持つクラスAを使っていたけれどint型変数1つで十分だとわかったから思い切ってAからintに変えてしまおう、といったときに「プリミティブ型は値渡し、リファレンス型は参照渡し」という仕様になっていた場合、まず代入の部分は全て書き換えなければならないでしょう。他にも書き換えなければならない部分が出てくることでしょう。しかし、C++方式なら、うまくいけば変数の宣言の型名をAからintに変えるだけで済む場合もあるでしょう。
クラスAとintの共通項だけを使って書かれたコードなら、C++に限らずいまの言語仕様のままで置き換え可能ですよね。
プリミティブ型が値渡しだからとか関係なく、引数でデータの受け渡しをするなら構造体にまとめる、というのが王道なのでは?
プリミティブ型一個だけだからまとめなくていいや、なんていう例外を許すほうが開発現場は混乱すると思いますが。


コネクションには、他にも例外がいっぱいあっていま以上に混乱を招くのではないでしょうか。
ポインタだとこのように書くところが

CODE:

void hoge1(int *p) {
    *pで書き換える
}
void hoge2(int **pp) {
    *pp = 新しい参照先;
}
int i;
int *p = &i;
int **pp = &p;
int ***ppp = &pp;
hoge1(&i);
hoge1(p);
hoge1(*pp);
hoge2(pp);
hoge2(*ppp);
コネクションだとこのようになるでしょうか。

CODE:

void hoge1(int $c){}
void hoge2(int $$cc){}
int i;
int $c(i);
int $$cc(c);
int $$$ccc(cc);
hoge1(i);   // おおもとを参照するときは意識しない
hoge1(c);   // 同上
hoge1(cc);  // 同上
hoge1(ccc); // 同上
hoge2(cc);   // コネクションが操作対象なら意識する(?)
hoge2($ccc); // 同上(?)
とりあえず思い付いただけで
・階層が1つのコネクションはエイリアスと区別が付かないので引数にできない
・階層が1つのコネクションはエイリアスなので、コネクションとして扱うときは宣言時と参照時で演算子の数が1つずれる
・$が一個の宣言と2個以上の宣言で意味が異なる
・引数の宣言と実引数のコネクションの階層数が同じときは実引数に$が付かないが階層数が違えば付く
といった感じです。
わたしにはポインタのほうが直感的に思えます。

コードが読むのにいちいち宣言を確認しないといけなくて読む気がしなくなるのではないでしょうか。
思い付くまま気の向くまま書けるようにはなるかもしれませんけど。

そもそも参照型は呼び出し側だけを見て書き換わるかどうかを判断できないという問題があります。
なので言語に依らず、基本的には書き換わらない前提で、書き換わる引数が明示的になるよう設計されていると思います。

コネクションの機能は分かりますが、設計においてどのようなメリットがあるのか分かりません。
具体例を示す必要があるのではないでしょうか。
最後に編集したユーザー ISLe on 2013年3月10日(日) 19:13 [ 編集 2 回目 ]

YuO
記事: 947
登録日時: 14年前

RE: C++とその他言語における値渡し・参照渡し

投稿記事 by YuO » 12年前

MoNoQLoREATOR さんが書きました:オブジェクト指向が前提の言語は、クラスのインスタンスがデフォルトで参照渡しとなっている傾向があるようです。
提示されたC++, Java, C#と,ActionScriptの元になるECMA Scriptに関して,全て基本的に「値渡し」です。
Java/C#の参照型 (class) は,参照型のインスタンスをそのまま変数に保持することがないだけですから。

参照渡しは,仮引数変数への代入によって実引数となった呼び出し元の変数に対する変更が行える物のことを言います。
C++だとreference,C#だとrefを使うのが参照渡しです。
# C++でpointer使うのは値渡し。
MoNoQLoREATOR さんが書きました:しかし、プリミティブ型とリファレンス型でデフォルトの動作が異なるというのは混乱を招くことに他ならないのではないでしょうか。
招かないでしょう。
MoNoQLoREATOR さんが書きました:例えば型を変更する必要が生じたとします。
それがプリミティブ型とリファレンス型の変更であった場合、ソースコードを大幅に書き換える必要が出てくる可能性も十分あるでしょう。
通常,言語プリミティブな型はimmutableになっています。
immutableな参照型に変更する限り,通常大幅に書き換える必要はありません。
immutableな型からmutableな型への変更を行うのであれば,それは設計の大幅変更ですから,ソースコードの大幅変更もありえます。
MoNoQLoREATOR さんが書きました:同じ理由で、C++のtemplateのようなこともできません。
templateのようなこと,とは?
C++のtemplateはコンテナ要素型指定からコンパイル時演算の黒魔術まで,チューリング完全であるがために色々なことが出来ます。
どのことを理由として,何がどの言語でできないのでしょうか。
# C#/JavaのGenericsはそもそもduck typingを指向していないので,duck typingができない,というのであれば的外れな指摘になります。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

Re: C++とその他言語における値渡し・参照渡し

投稿記事 by MoNoQLoREATOR » 12年前

返信ありがとうございます。

>>ISLeさん

すみません。ダブルコネクション以上のコネクションに対して参照先を指定する際の仕様が不十分でした。
受け取り側の深度が確定しているから良きに計らえばよいと考えていたのですが、そもそも関数の引数に指定する際は(関数オーバーロードにより)受け取り側の深度が確定していないばかりか、どんな型であるかすらも保障されていませんでした。
また「コネクションであるということを"意識する"」などという曖昧な方針で定義していたことも間違いでした。
まず定義を曖昧でないものにします。
コネクションは『普段は参照先のインスタンスそのものとして扱い、$を伴う時のみコネクションとして扱う』
ここで"$を伴う"という表現についても定義します。
変数の宣言時、$キーワードを付けて宣言するとその変数は$を伴っていることになります。参照先変更時、$=演算子を使っているため、その左辺は$を伴っていることになります。キャスト時、$キーワードを使ってキャストしているため$を伴っていることになります。

CODE:

int $ c;    // 宣言時$を伴う=コネクションとして扱う
int v;
c $= v;    // 参照先変更時$を伴う=コネクションとして扱う(左辺)
int $$ cc($c);    // キャスト時$を伴う=コネクションとして扱う
よって関数の引数にコネクションを指定する必要がある場合は$を使ってコネクションであることを明示しなければいけません。

CODE:

void func(int $$ cc){}
int $ c;
func($c);
以上から、ISLeさんのサンプルコードはこのようになります。

CODE:

void hoge1(int $c){}
void hoge2(int $$cc){}
int i;
int $c(i);
int $$cc($c);
int $$$ccc($$cc);
hoge1(i);   // おおもとを参照するときは意識しない
hoge1(c);   // 同上
hoge1(cc);  // 同上
hoge1(ccc); // 同上
hoge2($cc);
hoge2($ccc);
参照先を設定する時、$の数が受け取り側より参照先の方が1つ少ないことに違和感があるかもしれません。
これはC++の参照をベースにしているためです。
直感的でないのはここだけだと思います。
つまりダブルコネクション以上の参照先を設定する際に便利なようには作られていません。
ダブルコネクション以上のコネクションを使う機会はそこまで多くないはずですから、シングルコネクション使用時の利便性を優先しています。
シングルコネクションの簡潔さ、それによる直感的な利便性は自信を持って主張できます。

CODE:

int v;
int $ c;    // 宣言
c $= v;    // 参照先の変更
// あとはエイリアスとして使うだけ
これに関しては明らかにポインタよりも直感的です。

>>コードが読むのにいちいち宣言を確認しないといけなくて
>>読む気がしなくなるのではないでしょうか。

むしろコネクションよりもポインタの方が宣言を確認しないといけないのではないかと思います。
インスタンスにアクセスしたい場合、あなたは何もする必要がありません。
しかし、ポインタの場合は何重ポインタになっているのか宣言を見て確認し、何回ポインタ演算すればよいのかを知る必要があります。
階層を移動したい場合、キャストに使用した$の数と同じ階層に直ちに移動することができます。(自分より上の階層へ移動しようとした場合はエラーになりますがこれはポインタも同じようなもので、2つ以上上の階層へは移動できません)
しかし、ポインタの場合は何重ポインタになっているのか宣言を見て確認し、何回ポインタ演算すればよいのかを知る必要があります。

>>そもそも参照型は呼び出し側だけを見て…(以下略)
それに関しては一部を除いてその通りだと私も考えています。
ただ、少なくともC++の場合は自分が関数に指定した変数にconstがついているか否かで「書き換えられる可能性があるか否か」を呼び出し側だけで判断することができます。
もしIDEを使っているのなら簡単に関数定義を確認することもできますから、書き換えられるかどうかの判断はそう難しくないのではないでしょうか。

コネクションのメリットについては先程述べた通りです。


>>YuOさん

すみません。「値渡し・参照渡し」という言葉を使うべきではありませんでした。
=演算子によってインスタンスそのもののコピーを伴う挙動を指すつもりで「値渡し」、コピーを伴わない挙動を指すつもりで「参照渡し」という言葉を使いました。

また、「プリミティブ型・リファレンス型」という言葉を選んだのも誤解を生む原因になったかもしれません。
Javaではプリミティブ型とリファレンス型に大別されていてそういう名称を使えばいいのかと思ったのですが一般的にプリミティブ型というと、言語に初めから実装されている型を指すようですね。
これも、=演算子によってインスタンスそのもののコピーを伴う型を指すつもりで「プリミティブ型」、コピーを伴わない型を指すつもりで「リファレンス型」という言葉を使いました。
同じ=演算子を使っていてもこの型の違いによって挙動が違う仕様の場合はソースコードの書き換えが大変でしょう、と言いたかったわけです。

templateについて。
任意の型に対する共通の処理を記述しておくということができないのではないかと思っていましたが、他の言語でも、例えば全ての型をobject型にアップキャストできたりといった仕様を利用すればできそうですね。
この記述は忘れてください。

ISLe
記事: 2650
登録日時: 14年前

Re: C++とその他言語における値渡し・参照渡し

投稿記事 by ISLe » 12年前

MoNoQLoREATOR さんが書きました:よって関数の引数にコネクションを指定する必要がある場合は$を使ってコネクションであることを明示しなければいけません。
そうなると、実引数にシングルコネクションにキャストして与えたいときはどうするのですか。
MoNoQLoREATOR さんが書きました:よって関数の引数にコネクションを指定する必要がある場合は$を使ってコネクションであることを明示しなければいけません。
コネクションは参照に階層を持つことが特徴ですが、仮引数と実引数とで階層の表現に対称性が無いと混乱を招くのではないですか。
MoNoQLoREATOR さんが書きました:参照先を設定する時、$の数が受け取り側より参照先の方が1つ少ないことに違和感があるかもしれません。
これはC++の参照をベースにしているためです。
どうして「C++の参照をベースにするとコネクションの対称性が無くなる」のかを説明してもらえませんか。
C++の参照は他言語の参照と何か違うのでしょうか。
どの言語でも参照は実体のエイリアスであり繰り返し複製しても参照は常に直に実体を指すものですが。
MoNoQLoREATOR さんが書きました:つまりダブルコネクション以上の参照先を設定する際に便利なようには作られていません。
ダブルコネクション以上のコネクションを使う機会はそこまで多くないはずですから、シングルコネクション使用時の利便性を優先しています。
シングルコネクションの簡潔さ、それによる直感的な利便性は自信を持って主張できます。
コネクションは参照に階層を持つことが特徴ではないのですか。
MoNoQLoREATOR さんが書きました:しかし、ポインタの場合は何重ポインタになっているのか宣言を見て確認し、何回ポインタ演算すればよいのかを知る必要があります。
ポインタは~へのポインタ型として独立してますし、*を付けなければいけないのに付けなかったり付けてはいけないのに付けたりすればコンパイルエラーになります。
宣言を確認しなくても参照するコードに*が付いているかどうかで区別できるので、その場で迷いなく宣言を書き換える必要があるかポインタの代入だけで済ませられるか判断できます。
コネクションと実体の参照コードに違いはないので、コネクションとして扱うにはコネクションなのか実体なのか確認が必要になります。
MoNoQLoREATOR さんが書きました:もしIDEを使っているのなら簡単に関数定義を確認することもできますから、書き換えられるかどうかの判断はそう難しくないのではないでしょうか。
この点に関してコネクションのメリットは何もないということでしょうか。
MoNoQLoREATOR さんが書きました:コネクションのメリットについては先程述べた通りです。
コネクションならではのメリットが何も感じられません。
ダブルコネクション以上のコネクションを使う機会がほとんどないのなら、必要に応じて参照先を変えるのは従来の参照を引数として使えば事足ります。
必要に応じて参照先を変えるというのは関数化すべき場面でしょう。
ほとんどない機会にポインタを使ってもそれほど手間は掛からないわけですし。
最後に編集したユーザー ISLe on 2013年3月11日(月) 17:18 [ 編集 4 回目 ]

YuO
記事: 947
登録日時: 14年前

Re: C++とその他言語における値渡し・参照渡し

投稿記事 by YuO » 12年前

MoNoQLoREATOR さんが書きました:これも、=演算子によってインスタンスそのもののコピーを伴う型を指すつもりで「プリミティブ型」、コピーを伴わない型を指すつもりで「リファレンス型」という言葉を使いました。
同じ=演算子を使っていてもこの型の違いによって挙動が違う仕様の場合はソースコードの書き換えが大変でしょう、と言いたかったわけです。
うーん,現実的には
  • Javaにおいては全てインスタンスのコピーを伴わないとみなしてもかまわない
  • C#においては,「値型はimmutableにすべき」というMSのガイドラインを守る限り,全てインスタンスのコピーを伴わないとみなしてもかまわない
といえます。
immutable型は値をコピーしても共有しても同じことになるため,immutable型であるプリミティブ型に対してコピーしているということを考えず,すべて共有すると考えてしまってもよいわけです。

C++は,コピーによって,インスタンスがコピーされる場合と実質的にされない場合と部分的にコピーされる場合がある,やっかいな言語です。
  • デフォルトは全てコピー
  • std::shared_ptrのように,実質的に「コピーせずに共有する」戦略がとれる
  • コピーする部分と共有する部分を意図的に/意図せずに同一クラスに混ぜることが出来る
上記のように,代入において混乱が発生するのは混乱する可能性があるのは,MoNoQLoREATORさんの予想に反し,C++においてのみです。
全て,mutableな型に関して「インスタンスのコピーができる」という点から来ています。


さて,上記の視点に立って提案のコネクションを見てみましょう。
……何ら解決に役立つ物とは思えません。
そもそもの問題点の置き所が違う,といえばそれまでですが,コネクションは
  • 参照保持に特化したポインタという点ではC++の現行のreferenceと同じ
  • referenceに比べて初期化されなかったりnullptrだったりする可能性がある分,referenceに劣る
  • コネクションの再設定機能は関数中で使うなどにより簡単にdangling pointerを発生させる

    CODE:

    void func (int $$ c)
    {
        int n = 1;
        int $ c1;
        c1 $= n;
        c $= c1;
    }
    
    int main (void)
    {
        int $c;
        func($c);
        // ここでcは何と結合していますか?
        return c;
    }
と,劣化pointerとしてしか使えない,と見る事が出来ます。

ISLe
記事: 2650
登録日時: 14年前

Re: C++とその他言語における値渡し・参照渡し

投稿記事 by ISLe » 12年前

コネクションってポインタの間接参照演算子を使うところと使わないところを逆にしたものですよね。
だとすれば参照時に参照演算子を書かずに済むことが唯一のメリットということですかね。

でも逆になるということは書かなくて良かったところが書かなければならなくなるということです。
書かなくて良かったところというのは参照するコードを呼び出して利用する側です。
ふつうに考えて利用する側のほうが数が多くなるので労力の総量は増えると思いますが。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 14年前
住所: 東京

RE: C++とその他言語における値渡し・参照渡し

投稿記事 by MoNoQLoREATOR » 12年前

>>ISLeさん

>>そうなると、実引数にシングルコネクションにキャストして与えたいときはどうするのですか。

「何らかのコネクションをシングルコネクションにキャストしてそれを実引数として関数に与えたいときはどうするのですか」という意味の質問でしょうか?
もしそうなら、答えは既に示されています。
「よって関数の引数にコネクションを指定する必要がある場合は$を使ってコネクションであることを明示しなければいけません。」という文章の直後のサンプルコードがそれです。
いついかなる場合でもダブルコネクションの参照先に指定するのはシングルコネクションであり、その識別子の前には$が必ず1つだけつくことになります。

CODE:

void func(int $$ cc){}
int $ c;
int $$ cc;
int $$$ ccc;
func($c);
func($cc);
func($ccc);
>>コネクションは参照に階層を持つことが特徴ですが、
>>仮引数と実引数とで階層の表現に対称性が無いと混乱を招くのではないですか。
>>どうして「C++の参照をベースにするとコネクションの対称性が無くなる」のかを
>>説明してもらえませんか。(以下省略)

それはどうでしょうか。
現にC++の参照の文法は

CODE:

int v;
int & r = v;
こうであり、参照先を設定するときは=の左右で階層が違っています。
にもかかわらず混乱が招かれていると聞いたことはありません。
最も、「参照は2階層よりも階層が増えないから」という理由があるのかもしれません。
「C++の参照をベースにするとコネクションの対称性が無くなる」理由は上記の通りC++の参照がそうなっているからです。
しかし、無理にC++の参照に似せる理由はありません。
参照先設定の際に階層を揃える仕様にしてもコネクションの本質は変わりません。

>>コネクションは参照に階層を持つことが特徴ではないのですか。

特徴ではありますが最大の特徴ではありません。
最大の特徴は既に述べた通り「シングルコネクション時での簡潔さ、それによる直感的な利便性」です。
無限に階層を重ねていける仕様にすればさらに便利なためそうしました。
2階層までだった場合、それ以上階層を増やすには途中からポインタを使わなければならず、一貫性が無くなり著しく可読性が落ちるからです。

>>ポインタは~へのポインタ型として独立してますし、*を付けなければいけない(以下省略)

コネクションも同じです。
確かに仕様にはエラーになるとは書かれていませんでしたが、「~しなければならない」「~することは許されていない」という文面から察してください。

CODE:

int $ c;
int $$ cc(c);    // cに$を付けなければいけないのに付けていないからエラー
$c = 5;    // cに$を付けてはいけないのに付けているからエラー
>>宣言を確認しなくても参照するコードに*が付いているか(以下省略)

それはソースコードがある程度記述されている場合の話ですか?
それに、ポインタかポインタでないか、コネクションかコネクションでないかを瞬時に判断できるか否かの話のように聞こえます。
それはソースコードの内容によると思います。
主にインスタンスを取り扱う内容であったなら、ポインタの場合インスタンスを取り出すための*演算子が頻出するため確かにポインタであるか否か、又、何重ポインタであるか否かがすぐにわかることでしょう。
しかし主に参照先の設定を取り扱う内容であったなら、コネクションの場合階層を表すための$キーワードが頻出するためコネクションであることがするにわかります。

>>この点に関してコネクションのメリットは何もないということでしょうか。

その通りです。

>>コネクションならではのメリットが何も感じられません。(以下略)

感じ方は人それぞれですからISLeさんにとってはそうなのでしょう。
途中で参照先を変える必要がある場面は相当数あると思いますし、関数化することによってそれを全て解決できるとは思えません。
参照先を変更する際の手間はポインタを使おうがコネクションを使おうが差はないと思います。
そうであればインスタンスにアクセスする際に便利なコネクションを私は選びます。



>>YuOさん

各言語の代入演算子による挙動について、どのように整理すればスッキリするのかわかってきたような気がするのでそこの部分はもう少し考えさせてください。

コネクションは参照先を設定しないとnullに設定されるようになっています。
これは最も最初に定義されています。
このことはダブルコネクション以上についても適応されています。
また、nullについては記述を忘れていましたが、コネクション用にnullという特別な識別子が用意されていて、これをコネクションの参照先に明示的に設定することもできます。

dangling pointerについてはその名の通りポインタにも当てはまることです。
少なくともこの部分ではポインタより劣化しているとは言えません。
参照先を変更するときは$=演算子を使う必要があるため、$=が出てきたら不正な参照先を設定していないか注意すればよいことになるので、ポインタよりは幾分ミスを減らせるのではないかと思います。
その上$=を検索すれば参照先を変更している部分をすぐに見つけることができるのでその点においてはややポインタより優れているのではないかと。

以上のことから「劣化referenceである」という結論を出すのならまだわかりますが、「劣化pointerである」という結論を出すのは理解できません。
何故ならpointerよりも劣っている部分を挙げていないからです。



>>ISLeさん

唯一ではありませんが最大のメリットはそうですね。
私は「参照先を操作する頻度よりインスタンスにアクセスする頻度の方が多いだろうし、最終的な目的はそれなのだからそれに特化させるべきだ」という考えでこれを作ったのですが、確かに引数として渡す機会は多いですよね。
とは言っても$をつける必要があるのはダブルコネクション以上のコネクションに対して渡すときだけですから、どちらの労力が増えるかは設計によると思います。
シングルコネクションを主に使うという設計であれば確実に労力は減ります。

ISLe
記事: 2650
登録日時: 14年前

Re: C++とその他言語における値渡し・参照渡し

投稿記事 by ISLe » 12年前

MoNoQLoREATOR さんが書きました:「よって関数の引数にコネクションを指定する必要がある場合は$を使ってコネクションであることを明示しなければいけません。」という文章の直後のサンプルコードがそれです。
いついかなる場合でもダブルコネクションの参照先に指定するのはシングルコネクションであり、その識別子の前には$が必ず1つだけつくことになります。
わたしの疑問の答えになっていません。
funcの引数がダブルコネクションを要求する場合はどうなるのですか?

CODE:

void func(int $$$ cc){}
int $ c;
int $$ cc;
int $$$ ccc;
func($c);   // エラー?
func($cc);
func($ccc); // 上位?下位?どっち
MoNoQLoREATOR さんが書きました:現にC++の参照の文法は

CODE:

int v;
int & r = v;
こうであり、参照先を設定するときは=の左右で階層が違っています。
にもかかわらず混乱が招かれていると聞いたことはありません。
最も、「参照は2階層よりも階層が増えないから」という理由があるのかもしれません。
「C++の参照をベースにするとコネクションの対称性が無くなる」理由は上記の通りC++の参照がそうなっているからです。
しかし、無理にC++の参照に似せる理由はありません。
参照先設定の際に階層を揃える仕様にしてもコネクションの本質は変わりません。
C++の参照型を示す記号は宣言にしか現れませんからそもそも対称性を持たせる必要がありません。
むしろ似せるべき対象じゃないのに似せようとして破綻しているのが現状ですよね。


わたしの日本語が拙いせいだと思いますがこちらの意図がまったく伝わっていない気がします。
シングルコネクションの利便性を繰り返し力説されても聞きたいのはそこではないのです。
これ以上続けても平行線だと思いますのでこのへんで退散します。
最後に編集したユーザー ISLe on 2013年3月12日(火) 18:34 [ 編集 1 回目 ]