C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[9]: 出力引数を基底型で受け取れないのはどうして?


(過去ログ 64 を表示中)

[トピック内 15 記事 (1 - 15 表示)]  << 0 >>

■37062 / inTopicNo.1)  出力引数を基底型で受け取れないのはどうして?
  
□投稿者/ aetos (160回)-(2009/06/12(Fri) 12:06:17)
aetos さんの Web サイト

分類:[C#] 

2009/06/12(Fri) 12:07:58 編集(投稿者)
2009/06/12(Fri) 12:07:35 編集(投稿者)
2009/06/12(Fri) 12:07:02 編集(投稿者)

以下のコードはコンパイルできません( out b が out Derived に適合しません)。

class Base {}
class Derived : Base {}

void Hoge( out Derived d ) { ... }

Base b = null;
Hoge( out b );

何故なのでしょう?
「言語規格でそう決まっているから」ではなく、何故そう決まっているのかについて、推測でも構いませんので、ご存知でしたらご教示ください。

よろしくお願いします。
引用返信 編集キー/
■37065 / inTopicNo.2)  Re[1]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ みきぬ (467回)-(2009/06/12(Fri) 12:32:39)
1つ確認です。

out がなくてもビルドできず、同じエラーが出ましたが、
(Visual Studio 2005 にて「'Base' から 'Derived' に変換できません。」)

おそらくそれは承知の上で、out がついてるのになんでエラーになるのっていうのが質問の主旨なんですよね?
引用返信 編集キー/
■37066 / inTopicNo.3)  Re[2]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ aetos (161回)-(2009/06/12(Fri) 12:38:27)
aetos さんの Web サイト
No37065 (みきぬ さん) に返信
> 1つ確認です。
>
> out がなくてもビルドできず、同じエラーが出ましたが、
> (Visual Studio 2005 にて「'Base' から 'Derived' に変換できません。」)
>
> おそらくそれは承知の上で、out がついてるのになんでエラーになるのっていうのが質問の主旨なんですよね?

そうです。
入力の場合(out がない場合)、メソッド側が Base を取り、そこに呼び出し側で Derived を渡すことはできます。
out なんだから、その逆ができてもいいじゃん、と思うわけです。

ref は in を兼ねるので出来ないことと、out は呼び出し機構としては ref と等しいことから出来ない、というのはわかりますが、なんか気に入らない説明だなぁ、と思います。
せっかく ref と out を別々に用意しているのに、変なところで混同して台無しにしているというか。
引用返信 編集キー/
■37068 / inTopicNo.4)  Re[3]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ 囚人 (372回)-(2009/06/12(Fri) 12:49:09)
http://bbs.wankuma.com/index.cgi?mode=al2&namber=37050
でも書きましたけど、out Base と out Derived に互換性がないからじゃない?
継承関係は Base ← Derived だけど、out Base ← out Derived じゃない。

今回はポインタの例じゃないけど、ポインタを持ち出すと説明しやすいのでポインタという用語を使うと
・Base のポインタは Derived の「インスタンス」を指せる。
・Base のポインタのポインタは Derivedの「ポインタ」は指せない。
みたいな感じ?

引用返信 編集キー/
■37069 / inTopicNo.5)  Re[3]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ みきぬ (468回)-(2009/06/12(Fri) 12:58:13)
> ref は in を兼ねるので出来ないことと、out は呼び出し機構としては ref と等しいことから出来ない、というのはわかりますが、なんか気に入らない説明だなぁ、と思います。
> せっかく ref と out を別々に用意しているのに、変なところで混同して台無しにしているというか。

これは脱線ですが、

int i;
int.TryParse(s, out i);

これは、int.TryParse(s, out int i); とか1行で書かせてほしいなあと思うことはよくあります。
引用返信 編集キー/
■37070 / inTopicNo.6)  Re[4]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ aetos (163回)-(2009/06/12(Fri) 12:59:47)
aetos さんの Web サイト
No37069 (みきぬ さん) に返信
>>ref は in を兼ねるので出来ないことと、out は呼び出し機構としては ref と等しいことから出来ない、というのはわかりますが、なんか気に入らない説明だなぁ、と思います。
>>せっかく ref と out を別々に用意しているのに、変なところで混同して台無しにしているというか。
>
> これは脱線ですが、
>
> int i;
> int.TryParse(s, out i);
>
> これは、int.TryParse(s, out int i); とか1行で書かせてほしいなあと思うことはよくあります。

言語拡張の夢を語るなら、

[ bool success, int result ] = int.TryParse( s );

で。
引用返信 編集キー/
■37072 / inTopicNo.7)  Re[3]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ よねKEN (355回)-(2009/06/12(Fri) 13:00:40)
> 推測でも構いませんので
ということなので、以下、もちろん推測です。

言語仕様を考える場合に、統一感を重視するなら、
Derived Hoge() { ... }
Base b = Hoge();
ということができるのだから、

> void Hoge( out Derived d ) { ... }
>
> Base b = null;
> Hoge( out b );

ということができると自然ですね。

ということは以下の2つの可能性が考えられるのではないでしょうか。

1. (ILレベルで素直に実現できるが)単にoutを使った上記のようなパターンは考慮から漏れた。
2. ILレベルで素直には実現できないため、実現するとなると
コンパイラによるシンタックスシュガーとして実現する必要があるが、
そこまでは対応する必要なしと判断された。

2.のパターンを考えると
> Base b = null;
> Hoge( out b );
のコードを
Derived d = null;
Hoge( out d );
Base b = d;
と同じ意味として解釈して処理すれば、シンタックスシュガーとしてなら実現は可能ですね。

次。1.のパターンですが、out Derivedやref DerivedはILレベルでは、
Derived&として処理されるので、Derived&とBase&の間の変換が可能なのか
どうかとかが肝になりそうな気がします。

継承関係にあるクラス同志の&付き型のCLRでの扱いってどうなっているんでしょうね?
ILレベルで素直に実現できることなら、aetosさん提示仕様を実現してもよさそうですが、
CLRに手を入れないと素直には実現できない場合、上記のシンタックスシュガーの
パターンを適用する必要があり、そこまでやるかどうかの判断になってくるんでしょうね。
(ILレベルで素直に実現できない場合はそもそもaetosさん提示仕様のパターンは
考慮してなさそうな気がしますが)

というようなことを考えてみました。どうでしょう。
引用返信 編集キー/
■37077 / inTopicNo.8)  Re[4]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ aetos (164回)-(2009/06/12(Fri) 13:25:06)
aetos さんの Web サイト
No37068 (囚人 さん) に返信

ref と out が同じ、内部的にはおそらくはポインタを使った呼び出し機構なんだろうというのは承知の上で敢えて置いといて、out の場合は出力と明示しているんだから、なにもポインタの汎用的なルールを引きずらなくてもいいのになぁ、と思うわけです。

引用返信 編集キー/
■37078 / inTopicNo.9)  Re[4]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ aetos (165回)-(2009/06/12(Fri) 13:34:48)
aetos さんの Web サイト
No37072 (よねKEN さん) に返信
> ということができると自然ですね。

ですです。

> 次。1.のパターンですが、out Derivedやref DerivedはILレベルでは、
> Derived&として処理されるので、Derived&とBase&の間の変換が可能なのか
> どうかとかが肝になりそうな気がします。

囚人さんがおっしゃるように、汎用的に変換を認めてしまうと問題になると思います。
ただし、out の場合には限定して出来てもよいのではないか、と思ったのです。
引用返信 編集キー/
■37080 / inTopicNo.10)  Re[5]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ aetos (166回)-(2009/06/12(Fri) 13:37:02)
aetos さんの Web サイト
No37078 (aetos さん) に返信

ちなみに C++ ですと、

class Base
{
public:
	virtual void Test() { printf( "I'm Base !!\n" ); }
};

class Derived : public Base
{
public:
	virtual void Test() { printf( "I'm Derived !!\n" ); }
};

int _tmain(int argc, _TCHAR* argv[])
{
	Derived d;
	Derived * pd = &d;

	// これが暗黙的に認められたとすると…
	Base ** ppb = reinterpret_cast< Base ** >( &pd );

	Base b;
	*ppb = &b;

	pd->Test(); // I'm Base !!
	(*ppb)->Test(); // I'm Base !!

	return 0;
}

pd->Test(); も I'm Base !! になっちゃいます。これはまずい。

引用返信 編集キー/
■37081 / inTopicNo.11)  Re[5]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ 囚人 (373回)-(2009/06/12(Fri) 13:38:44)
ん〜実装が難しいとかそんな理由かな(知らんけど)。
データに継承関係(互換性)があったら、それらのメタデータ同士にも継承関係(互換性)があるべきってのは難しいんじゃない?

out に限っては、確かにできてもいいじゃんって気はしますが、できたらできたで混乱しそう。

引用返信 編集キー/
■37101 / inTopicNo.12)  Re[6]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ なちゃ (294回)-(2009/06/12(Fri) 17:20:29)
outとrefに内部的な仕組みの差異がなくて、あくまでC#言語の範疇で属性を解釈してるだけだからだと思います。
あとは、コンパイラレベルでシンタックスシュガーを提供するかどうかですが、
多分シンプルなままにしておいたってだけな気がします。
他言語との絡みで混乱の元になるかもしれませんし。

引用返信 編集キー/
■37102 / inTopicNo.13)  Re[7]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ よねKEN (356回)-(2009/06/12(Fri) 17:53:25)
No37101 (なちゃ さん) に返信
> outとrefに内部的な仕組みの差異がなくて、あくまでC#言語の範疇で属性を解釈してるだけだからだと思います。

http://msdn.microsoft.com/ja-jp/library/system.reflection.parameterattributes(VS.95).aspx
http://msdn.microsoft.com/ja-jp/library/system.reflection.parameterinfo.attributes(VS.95).aspx

out/refが付いている場合は指定した型の&付きの型として扱われ(例:System.Int32ならSystem.Int32&となる)、ParameterInfo.Attributes(パラメータのメタデータ)がParameterAttributes.Outの属性を持っているという違いのことですね。


引用返信 編集キー/
■37215 / inTopicNo.14)  Re[8]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ streetw (6回)-(2009/06/15(Mon) 18:15:24)
class Base {}
class Derived1: Base {}
class Derived2: Base {}

void Test(out Derived1 p) {...}
void Test(out Derived2 p) {...}

Base b;
Test(out b)

オーバーロードの解決がややこしくならないでしょうか?
普通なら完全に一致するシグネチャがないときは派生もとの型で調べていけばいいですが、
outのときは派生先をたどることになるので、新たなルールが必要になると思います。
あとたぶん、よねKENさんの書かれたシンタックスシュガーも必要とか?
でも、これを使う場面は少ないはずだから、それだったらエラーにしちゃって、
プログラマーに2行追加してもらった方がシンプルと決断されたんじゃないでしょうか?

outはrefのおまけなので、そこまで要求しないで!という本音を想像しましたw

引用返信 編集キー/
■37233 / inTopicNo.15)  Re[9]: 出力引数を基底型で受け取れないのはどうして?
□投稿者/ aetos (170回)-(2009/06/15(Mon) 23:42:15)
aetos さんの Web サイト
No37215 (streetw さん) に返信

> オーバーロードの解決がややこしくならないでしょうか?

なーるほどー。目から鱗です。

> outはrefのおまけなので、そこまで要求しないで!という本音を想像しましたw

そうだろうと思うけどさぁw
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -